Text highlighter optimizations

This commit is contained in:
serso 2016-01-07 12:11:07 +01:00
parent 25c83bac94
commit 2215b11e6f
11 changed files with 191 additions and 261 deletions

View File

@ -22,6 +22,8 @@
package org.solovyev.android.calculator;
import android.text.SpannableStringBuilder;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.common.text.Strings;
@ -35,7 +37,7 @@ import jscl.NumeralBase;
* Date: 12/15/11
* Time: 9:01 PM
*/
public abstract class AbstractNumberBuilder {
public abstract class BaseNumberBuilder {
@Nonnull
protected final CalculatorEngine engine;
@ -46,7 +48,7 @@ public abstract class AbstractNumberBuilder {
@Nullable
protected NumeralBase nb;
protected AbstractNumberBuilder(@Nonnull CalculatorEngine engine) {
protected BaseNumberBuilder(@Nonnull CalculatorEngine engine) {
this.engine = engine;
this.nb = engine.getNumeralBase();
}
@ -58,15 +60,14 @@ public abstract class AbstractNumberBuilder {
* @return true if we can continue of processing of current number, if false - new number should be constructed
*/
protected boolean canContinue(@Nonnull MathType.Result mathTypeResult) {
boolean result = mathTypeResult.getMathType().getGroupType() == MathType.MathGroupType.number &&
return mathTypeResult.type.getGroupType() == MathType.MathGroupType.number &&
!spaceBefore(mathTypeResult) &&
numeralBaseCheck(mathTypeResult) &&
numeralBaseInTheStart(mathTypeResult.getMathType()) || isSignAfterE(mathTypeResult);
return result;
numeralBaseInTheStart(mathTypeResult.type) || isSignAfterE(mathTypeResult);
}
private boolean spaceBefore(@Nonnull MathType.Result mathTypeResult) {
return numberBuilder == null && Strings.isEmpty(mathTypeResult.getMatch().trim());
return numberBuilder == null && Strings.isEmpty(mathTypeResult.match.trim());
}
private boolean numeralBaseInTheStart(@Nonnull MathType mathType) {
@ -74,12 +75,12 @@ public abstract class AbstractNumberBuilder {
}
private boolean numeralBaseCheck(@Nonnull MathType.Result mathType) {
return mathType.getMathType() != MathType.digit || getNumeralBase().getAcceptableCharacters().contains(mathType.getMatch().charAt(0));
return mathType.type != MathType.digit || getNumeralBase().getAcceptableCharacters().contains(mathType.match.charAt(0));
}
private boolean isSignAfterE(@Nonnull MathType.Result mathTypeResult) {
if (!isHexMode()) {
final String match = mathTypeResult.getMatch();
final String match = mathTypeResult.match;
if ("".equals(match) || "-".equals(match) || "+".equals(match)) {
final StringBuilder localNb = numberBuilder;
if (localNb != null && localNb.length() > 0) {
@ -100,4 +101,6 @@ public abstract class AbstractNumberBuilder {
protected NumeralBase getNumeralBase() {
return nb == null ? engine.getNumeralBase() : nb;
}
public abstract int process(@Nonnull SpannableStringBuilder sb, @Nonnull MathType.Result result);
}

View File

@ -63,7 +63,7 @@ public class CalculatorKeyboardImpl implements CalculatorKeyboard {
final StringBuilder textToBeInserted = new StringBuilder(text);
final MathType.Result mathType = MathType.getType(text, 0, false);
switch (mathType.getMathType()) {
switch (mathType.type) {
case function:
textToBeInserted.append("()");
cursorPositionOffset = -1;
@ -78,7 +78,7 @@ public class CalculatorKeyboardImpl implements CalculatorKeyboard {
}
if (cursorPositionOffset == 0) {
if (MathType.openGroupSymbols.contains(text)) {
if (MathType.groupSymbols.contains(text)) {
cursorPositionOffset = -1;
}
}

View File

@ -22,6 +22,8 @@
package org.solovyev.android.calculator;
import android.text.SpannableStringBuilder;
import org.solovyev.android.calculator.math.MathType;
import javax.annotation.Nonnull;
@ -34,27 +36,33 @@ import jscl.NumeralBase;
* Time: 8:33 PM
*/
public class LiteNumberBuilder extends AbstractNumberBuilder {
public class LiteNumberBuilder extends BaseNumberBuilder {
public LiteNumberBuilder(@Nonnull CalculatorEngine engine) {
super(engine);
this.nb = engine.getNumeralBase();
}
public void process(@Nonnull MathType.Result mathTypeResult) {
if (canContinue(mathTypeResult)) {
@Override
public int process(@Nonnull SpannableStringBuilder sb, @Nonnull MathType.Result result) {
process(result);
return 0;
}
public void process(@Nonnull MathType.Result result) {
if (canContinue(result)) {
// let's continue building number
if (numberBuilder == null) {
// if new number => create new builder
numberBuilder = new StringBuilder();
}
if (mathTypeResult.getMathType() != MathType.numeral_base) {
if (result.type != MathType.numeral_base) {
// just add matching string
numberBuilder.append(mathTypeResult.getMatch());
numberBuilder.append(result.match);
} else {
// set explicitly numeral base (do not include it into number)
nb = NumeralBase.getByPrefix(mathTypeResult.getMatch());
nb = NumeralBase.getByPrefix(result.match);
}
} else {

View File

@ -22,8 +22,9 @@
package org.solovyev.android.calculator;
import android.text.SpannableStringBuilder;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.common.MutableObject;
import java.util.ArrayList;
import java.util.List;
@ -46,35 +47,28 @@ import jscl.text.Parser;
* Date: 10/23/11
* Time: 2:57 PM
*/
public class NumberBuilder extends AbstractNumberBuilder {
public class NumberBuilder extends BaseNumberBuilder {
public NumberBuilder(@Nonnull CalculatorEngine engine) {
super(engine);
}
@Nullable
private static MathType.Result replaceNumberInText(@Nonnull StringBuilder text,
private static int replaceNumberInText(@Nonnull SpannableStringBuilder sb,
@Nullable String number,
int trimmedChars,
@Nullable MutableObject<Integer> offset,
@Nonnull NumeralBase nb,
@Nonnull final MathEngine engine) {
MathType.Result result = null;
if (number != null) {
if (number == null) {
return 0;
}
// in any case remove old number from text
final int oldNumberLength = number.length() + trimmedChars;
text.delete(text.length() - oldNumberLength, text.length());
sb.delete(sb.length() - oldNumberLength, sb.length());
final String newNumber = formatNumber(number, nb, engine);
if (offset != null) {
// register offset between old number and new number
offset.setObject(newNumber.length() - oldNumberLength);
}
text.append(newNumber);
}
return result;
sb.append(newNumber);
// offset between old number and new number
return newNumber.length() - oldNumberLength;
}
@Nonnull
@ -138,47 +132,40 @@ public class NumberBuilder extends AbstractNumberBuilder {
/**
* Method replaces number in text according to some rules (e.g. formatting)
*
* @param text text where number can be replaced
* @param mathTypeResult math type result of current token
* @param offset offset between new number length and old number length (newNumberLength - oldNumberLength)
* @return new math type result (as one can be changed due to substituting of number with constant)
* @param sb text where number can be replaced
* @param result math type result of current token
* @return offset between new number length and old number length (newNumberLength - oldNumberLength)
*/
@Nonnull
public MathType.Result process(@Nonnull StringBuilder text, @Nonnull MathType.Result mathTypeResult, @Nullable MutableObject<Integer> offset) {
final MathType.Result possibleResult;
if (canContinue(mathTypeResult)) {
@Override
public int process(@Nonnull SpannableStringBuilder sb, @Nonnull MathType.Result result) {
if (canContinue(result)) {
// let's continue building number
if (numberBuilder == null) {
// if new number => create new builder
numberBuilder = new StringBuilder();
}
if (mathTypeResult.getMathType() != MathType.numeral_base) {
if (result.type != MathType.numeral_base) {
// just add matching string
numberBuilder.append(mathTypeResult.getMatch());
numberBuilder.append(result.match);
} else {
// set explicitly numeral base (do not include it into number)
nb = NumeralBase.getByPrefix(mathTypeResult.getMatch());
nb = NumeralBase.getByPrefix(result.match);
}
possibleResult = null;
return 0;
} else {
// process current number (and go to the next one)
possibleResult = processNumber(text, offset);
return processNumber(sb);
}
return possibleResult == null ? mathTypeResult : possibleResult;
}
/**
* Method replaces number in text according to some rules (e.g. formatting)
*
* @param text text where number can be replaced
* @param offset offset between new number length and old number length (newNumberLength - oldNumberLength)
* @return new math type result (as one can be changed due to substituting of number with constant)
* @param sb text where number can be replaced
* @return offset between new number length and old number length (newNumberLength - oldNumberLength)
*/
@Nullable
public MathType.Result processNumber(@Nonnull StringBuilder text, @Nullable MutableObject<Integer> offset) {
public int processNumber(@Nonnull SpannableStringBuilder sb) {
// total number of trimmed chars
int trimmedChars = 0;
@ -216,6 +203,6 @@ public class NumberBuilder extends AbstractNumberBuilder {
nb = engine.getNumeralBase();
}
return replaceNumberInText(text, number, trimmedChars, offset, localNb, engine.getMathEngine0());
return replaceNumberInText(sb, number, trimmedChars, localNb, engine.getMathEngine0());
}
}

View File

@ -78,20 +78,20 @@ public class ToJsclTextProcessor implements TextProcessor<PreparedExpression, St
if (mathTypeBefore != null) {
final MathType current = mathTypeResult.getMathType();
final MathType current = mathTypeResult.type;
if (current.isNeedMultiplicationSignBefore(mathTypeBefore.getMathType())) {
if (current.isNeedMultiplicationSignBefore(mathTypeBefore.type)) {
result.append("*");
}
}
if (mathTypeBefore != null &&
(mathTypeBefore.getMathType() == MathType.function || mathTypeBefore.getMathType() == MathType.operator) &&
Collections.find(MathType.openGroupSymbols, startsWithFinder) != null) {
final String functionName = mathTypeBefore.getMatch();
(mathTypeBefore.type == MathType.function || mathTypeBefore.type == MathType.operator) &&
Collections.find(MathType.groupSymbols, startsWithFinder) != null) {
final String functionName = mathTypeBefore.match;
final Function function = Locator.getInstance().getEngine().getFunctionsRegistry().get(functionName);
if (function == null || function.getMinParameters() > 0) {
throw new CalculatorParseException(i, s, new CalculatorMessage(CalculatorMessages.msg_005, MessageType.error, mathTypeBefore.getMatch()));
throw new CalculatorParseException(i, s, new CalculatorMessage(CalculatorMessages.msg_005, MessageType.error, mathTypeBefore.match));
}
}

View File

@ -129,7 +129,7 @@ public enum MathType {
}
},
open_group_symbol(800, true, false, MathGroupType.other, "[", "(", "{") {
open_group_symbol(800, true, false, MathGroupType.other, "(", "[", "{") {
@Override
public boolean isNeedMultiplicationSignBefore(@Nonnull MathType mathTypeBefore) {
return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != function && mathTypeBefore != operator;
@ -141,7 +141,7 @@ public enum MathType {
}
},
close_group_symbol(900, false, true, MathGroupType.other, "]", ")", "}") {
close_group_symbol(900, false, true, MathGroupType.other, ")", "]", "}") {
@Override
public boolean isNeedMultiplicationSignBefore(@Nonnull MathType mathTypeBefore) {
return false;
@ -223,68 +223,10 @@ public enum MathType {
}
};
public static final List<String> openGroupSymbols = Arrays.asList("[]", "()", "{}");
public static final List<String> groupSymbols = Arrays.asList("()", "[]", "{}");
public final static Character POWER_10 = 'E';
public static final String IMAGINARY_NUMBER = "i";
public static final String IMAGINARY_NUMBER_JSCL = "√(-1)";
public static final String PI = "π";
public static final String E = "e";
public static final String C = "c";
public static final Double C_VALUE = 299792458d;
public static final String G = "G";
/* public static int getPostfixFunctionStart(@Nonnull CharSequence s, int position) throws ParseException {
assert s.length() > position;
int numberOfOpenGroups = 0;
int result = position;
for (; result >= 0; result--) {
final MathType mathType = getType(s.toString(), result).getMathType();
if (Collections.contains(mathType, digit, dot, grouping_separator, power_10)) {
// continue
} else if (mathType == close_group_symbol) {
numberOfOpenGroups++;
} else if (mathType == open_group_symbol) {
if (numberOfOpenGroups > 0) {
numberOfOpenGroups--;
} else {
break;
}
} else {
if (stop(s, numberOfOpenGroups, result)) break;
}
}
if (numberOfOpenGroups != 0){
throw new ParseException("Could not find start of prefix function!");
}
return result;
}
public static boolean stop(CharSequence s, int numberOfOpenGroups, int i) {
if (numberOfOpenGroups == 0) {
if (i > 0) {
final EndsWithFinder endsWithFinder = new EndsWithFinder(s);
endsWithFinder.setI(i + 1);
if (!Collections.contains(function.getTokens(), FilterType.included, endsWithFinder)) {
MathType type = getType(s.toString(), i).getMathType();
if (type != constant) {
return true;
}
}
} else {
return true;
}
}
return false;
}*/
public static final Double G_VALUE = 6.6738480E-11;
public static final String H_REDUCED = "h";
public static final Double H_REDUCED_VALUE = 6.6260695729E-34 / (2 * Math.PI);
public final static String NAN = "NaN";
public final static String INFINITY = "";
public final static String INFINITY_JSCL = "Infinity";
@ -425,6 +367,14 @@ public enum MathType {
return null;
}
public static boolean isOpenGroupSymbol(char c) {
return c == '(' || c == '[' || c == '{';
}
public static boolean isCloseGroupSymbol(char c) {
return c == ')' || c == ']' || c == '}';
}
public static enum MathGroupType {
function,
number,
@ -435,33 +385,23 @@ public enum MathType {
public static class Result {
@Nonnull
private final MathType mathType;
public final MathType type;
@Nonnull
private final String match;
public final String match;
public Result(@Nonnull MathType mathType, @Nonnull String match) {
this.mathType = mathType;
public Result(@Nonnull MathType type, @Nonnull String match) {
this.type = type;
this.match = match;
}
public int processToJscl(@Nonnull StringBuilder result, int i) throws CalculatorParseException {
return mathType.processToJscl(result, i, match);
return type.processToJscl(result, i, match);
}
public int processFromJscl(@Nonnull StringBuilder result, int i) {
return mathType.processFromJscl(result, i, match);
}
@Nonnull
public String getMatch() {
return match;
}
@Nonnull
public MathType getMathType() {
return mathType;
return type.processFromJscl(result, i, match);
}
}

View File

@ -120,7 +120,7 @@ public class VarEditorSaver<T extends MathEntity> implements View.OnClickListene
if (canBeSaved) {
final MathType.Result mathType = MathType.getType(name, 0, false);
if (mathType.getMathType() == MathType.text || mathType.getMathType() == MathType.constant) {
if (mathType.type == MathType.text || mathType.type == MathType.constant) {
if (Strings.isEmpty(value)) {
// value is empty => undefined variable

View File

@ -81,14 +81,14 @@ public class FromJsclSimplifyTextProcessor implements TextProcessor<String, Gene
mathTypeAfter = null;
}
if (needMultiplicationSign(mathTypeBefore == null ? null : mathTypeBefore.getMathType(), mathTypeAfter == null ? null : mathTypeAfter.getMathType())) {
if (needMultiplicationSign(mathTypeBefore == null ? null : mathTypeBefore.type, mathTypeAfter == null ? null : mathTypeAfter.type)) {
sb.append(Locator.getInstance().getEngine().getMultiplicationSign());
}
} else {
if (mathType.getMathType() == MathType.constant || mathType.getMathType() == MathType.function || mathType.getMathType() == MathType.operator) {
sb.append(mathType.getMatch());
i += mathType.getMatch().length() - 1;
if (mathType.type == MathType.constant || mathType.type == MathType.function || mathType.type == MathType.operator) {
sb.append(mathType.match);
i += mathType.match.length() - 1;
} else {
sb.append(ch);
}

View File

@ -4,7 +4,6 @@ import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.text.Html;
import android.util.Log;
import org.solovyev.android.calculator.App;
@ -50,7 +49,7 @@ public final class EditorTextProcessor implements TextProcessor<TextProcessorEdi
try {
final TextProcessorEditorResult processesText = getTextHighlighter().process(text);
result = new TextProcessorEditorResult(Html.fromHtml(processesText.toString()), processesText.getOffset());
result = new TextProcessorEditorResult(processesText.getCharSequence(), processesText.getOffset());
} catch (CalculatorParseException e) {
// set raw text
result = new TextProcessorEditorResult(text, 0);

View File

@ -22,15 +22,28 @@
package org.solovyev.android.calculator.view;
import org.solovyev.android.calculator.*;
import android.graphics.Typeface;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import com.google.common.collect.Lists;
import org.solovyev.android.Check;
import org.solovyev.android.calculator.BaseNumberBuilder;
import org.solovyev.android.calculator.CalculatorEngine;
import org.solovyev.android.calculator.CalculatorParseException;
import org.solovyev.android.calculator.LiteNumberBuilder;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.NumberBuilder;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.android.calculator.text.TextProcessor;
import org.solovyev.android.calculator.text.TextProcessorEditorResult;
import org.solovyev.common.MutableObject;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Map;
/**
* User: serso
@ -80,148 +93,128 @@ public class TextHighlighter implements TextProcessor<TextProcessorEditorResult,
@Nonnull
@Override
public TextProcessorEditorResult process(@Nonnull String text) throws CalculatorParseException {
final CharSequence result;
final CalculatorEngine engine = Locator.getInstance().getEngine();
final SpannableStringBuilder sb = new SpannableStringBuilder();
final BaseNumberBuilder nb = !formatNumber ? new LiteNumberBuilder(engine) : new NumberBuilder(engine);
int maxNumberOfOpenGroupSymbols = 0;
int numberOfOpenGroupSymbols = 0;
int offset = 0;
int groupsCount = 0;
int openGroupsCount = 0;
final StringBuilder text1 = new StringBuilder(5 * text.length());
int resultOffset = 0;
final AbstractNumberBuilder numberBuilder;
if (!formatNumber) {
numberBuilder = new LiteNumberBuilder(Locator.getInstance().getEngine());
} else {
numberBuilder = new NumberBuilder(Locator.getInstance().getEngine());
}
for (int i = 0; i < text.length(); i++) {
MathType.Result mathType = MathType.getType(text, i, numberBuilder.isHexMode());
final MathType.Result result = MathType.getType(text, i, nb.isHexMode());
if (numberBuilder instanceof NumberBuilder) {
final MutableObject<Integer> numberOffset = new MutableObject<>(0);
((NumberBuilder) numberBuilder).process(text1, mathType, numberOffset);
resultOffset += numberOffset.getObject();
} else {
((LiteNumberBuilder) numberBuilder).process(mathType);
}
offset += nb.process(sb, result);
final String match = mathType.getMatch();
switch (mathType.getMathType()) {
final String match = result.match;
switch (result.type) {
case open_group_symbol:
numberOfOpenGroupSymbols++;
maxNumberOfOpenGroupSymbols = Math.max(maxNumberOfOpenGroupSymbols, numberOfOpenGroupSymbols);
text1.append(text.charAt(i));
openGroupsCount++;
groupsCount = Math.max(groupsCount, openGroupsCount);
sb.append(text.charAt(i));
break;
case close_group_symbol:
numberOfOpenGroupSymbols--;
text1.append(text.charAt(i));
openGroupsCount--;
sb.append(text.charAt(i));
break;
case operator:
text1.append(match);
if (match.length() > 1) {
i += match.length() - 1;
}
i += append(sb, match);
break;
case function:
i = processHighlightedText(text1, i, match, "i", null);
i += append(sb, match);
makeItalic(sb, i + 1 - match.length(), i + 1);
break;
case constant:
i = processHighlightedText(text1, i, match, "b", null);
break;
case numeral_base:
i = processHighlightedText(text1, i, match, "b", null);
i += append(sb, match);
makeBold(sb, i + 1 - match.length(), i + 1);
break;
default:
if (mathType.getMathType() == MathType.text || match.length() <= 1) {
text1.append(text.charAt(i));
if (result.type == MathType.text || match.length() <= 1) {
sb.append(text.charAt(i));
} else {
text1.append(match);
i += match.length() - 1;
i += append(sb, match);
}
}
}
if (numberBuilder instanceof NumberBuilder) {
final MutableObject<Integer> numberOffset = new MutableObject<Integer>(0);
((NumberBuilder) numberBuilder).processNumber(text1, numberOffset);
resultOffset += numberOffset.getObject();
if (nb instanceof NumberBuilder) {
offset += ((NumberBuilder) nb).processNumber(sb);
}
if (maxNumberOfOpenGroupSymbols > 0) {
final StringBuilder text2 = new StringBuilder(text1.length());
int i = processBracketGroup(text2, text1, 0, 0, maxNumberOfOpenGroupSymbols);
for (; i < text1.length(); i++) {
text2.append(text1.charAt(i));
if (groupsCount == 0) {
return new TextProcessorEditorResult(sb, offset);
}
final List<GroupSpan> groupSpans = new ArrayList<>(groupsCount);
fillGroupSpans(sb, 0, 0, groupsCount, groupSpans);
for (GroupSpan groupSpan : Lists.reverse(groupSpans)) {
makeColor(sb, groupSpan.start, groupSpan.end, getColor(groupSpan.group, groupsCount));
}
return new TextProcessorEditorResult(sb, offset);
}
result = text2.toString();
} else {
result = text1.toString();
}
return new TextProcessorEditorResult(result, resultOffset);
}
private int processHighlightedText(@Nonnull StringBuilder result, int i, @Nonnull String match, @Nonnull String tag, @Nullable Map<String, String> tagAttributes) {
result.append("<").append(tag);
if (tagAttributes != null && !tagAttributes.entrySet().isEmpty()) {
for (Map.Entry<String, String> entry : tagAttributes.entrySet()) {
// attr1="attr1_value" attr2="attr2_value"
result.append(" ").append(entry.getKey()).append("=\"").append(entry.getValue()).append("\"");
}
}
result.append(">").append(match).append("</").append(tag).append(">");
private int append(SpannableStringBuilder t, String match) {
t.append(match);
if (match.length() > 1) {
return i + match.length() - 1;
} else {
return match.length() - 1;
}
return 0;
}
private static void makeItalic(@Nonnull SpannableStringBuilder t, int start, int end) {
t.setSpan(new StyleSpan(Typeface.ITALIC), start, end, SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
}
private static void makeBold(@Nonnull SpannableStringBuilder t, int start, int end) {
t.setSpan(new StyleSpan(Typeface.BOLD), start, end, SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
}
private static void makeColor(@Nonnull SpannableStringBuilder t, int start, int end, int color) {
t.setSpan(new ForegroundColorSpan(color), start, end, SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
}
private int fillGroupSpans(@Nonnull SpannableStringBuilder sb, int start, int group, int groupsCount, @Nonnull List<GroupSpan> spans) {
for (int i = start; i < sb.length(); i++) {
final char c = sb.charAt(i);
if (MathType.isOpenGroupSymbol(c)) {
i = highlightGroup(sb, i, group + 1, groupsCount, spans);
} else if (MathType.isCloseGroupSymbol(c)) {
return i;
}
}
private int processBracketGroup(@Nonnull StringBuilder result, @Nonnull CharSequence s, int i, int numberOfOpenings, int maxNumberOfGroups) {
result.append("<font color=\"").append(getColor(maxNumberOfGroups, numberOfOpenings)).append("\">");
for (; i < s.length(); i++) {
char ch = s.charAt(i);
String strCh = String.valueOf(ch);
if (MathType.open_group_symbol.getTokens().contains(strCh)) {
result.append(ch);
result.append("</font>");
i = processBracketGroup(result, s, i + 1, numberOfOpenings + 1, maxNumberOfGroups);
result.append("<font color=\"").append(getColor(maxNumberOfGroups, numberOfOpenings)).append("\">");
if (i < s.length() && MathType.close_group_symbol.getTokens().contains(String.valueOf(s.charAt(i)))) {
result.append(s.charAt(i));
}
} else if (MathType.close_group_symbol.getTokens().contains(strCh)) {
break;
} else {
result.append(ch);
}
return sb.length();
}
result.append("</font>");
return i;
private int highlightGroup(SpannableStringBuilder sb, int start, int group, int groupsCount, @Nonnull List<GroupSpan> spans) {
final int end = fillGroupSpans(sb, start + 1, group, groupsCount, spans);
if (start + 1 < end) {
spans.add(new GroupSpan(start + 1, end, group));
}
return end;
}
private String getColor(int totalNumberOfOpenings, int numberOfOpenings) {
int offset = ((int) (255 * 0.8)) * numberOfOpenings / (totalNumberOfOpenings + 1);
private int getColor(int group, int groupsCount) {
int offset = ((int) (255 * 0.8)) * group / (groupsCount + 1);
if (!dark) {
offset = -offset;
}
// for tests:
// int result = Color.rgb(BASE_COLOUR_RED_COMPONENT - offset, BASE_COLOUR_GREEN_COMPONENT - offset, BASE_COLOUR_BLUE_COMPONENT - offset);
int result = (0xFF << 24) | ((red + offset) << 16) | ((green + offset) << 8) | (blue + offset);
return (0xFF << 24) | ((red + offset) << 16) | ((green + offset) << 8) | (blue + offset);
}
return "#" + App.toColorString(result);
private static class GroupSpan {
final int start;
final int end;
final int group;
private GroupSpan(int start, int end, int group) {
Check.isTrue(start < end);
this.start = start;
this.end = end;
this.group = group;
}
}
}

View File

@ -45,29 +45,29 @@ public class MathTypeTest extends AbstractCalculatorTest {
@Test
public void testGetType() throws Exception {
assertEquals(MathType.function, MathType.getType("sin", 0, false).getMathType());
assertEquals(MathType.text, MathType.getType("sn", 0, false).getMathType());
assertEquals(MathType.text, MathType.getType("s", 0, false).getMathType());
assertEquals(MathType.text, MathType.getType("", 0, false).getMathType());
assertEquals(MathType.function, MathType.getType("sin", 0, false).type);
assertEquals(MathType.text, MathType.getType("sn", 0, false).type);
assertEquals(MathType.text, MathType.getType("s", 0, false).type);
assertEquals(MathType.text, MathType.getType("", 0, false).type);
try {
assertEquals(MathType.text, MathType.getType("22", -1, false).getMathType());
assertEquals(MathType.text, MathType.getType("22", -1, false).type);
Assert.fail();
} catch (IllegalArgumentException e) {
}
try {
assertEquals(MathType.text, MathType.getType("22", 2, false).getMathType());
assertEquals(MathType.text, MathType.getType("22", 2, false).type);
Assert.fail();
} catch (IllegalArgumentException e) {
}
assertEquals("atanh", MathType.getType("atanh", 0, false).getMatch());
assertEquals("atanh", MathType.getType("atanh", 0, false).match);
}
@Test
public void testPostfixFunctionsProcessing() throws Exception {
assertEquals(postfix_function, MathType.getType("5!", 1, false).getMathType());
assertEquals(postfix_function, MathType.getType("!", 0, false).getMathType());
assertEquals(postfix_function, MathType.getType("5!", 1, false).type);
assertEquals(postfix_function, MathType.getType("!", 0, false).type);
}
}