Changes
This commit is contained in:
parent
8c56dd1083
commit
89680ef544
@ -28,17 +28,14 @@ import android.text.TextUtils;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import com.squareup.otto.Bus;
|
import com.squareup.otto.Bus;
|
||||||
import com.squareup.otto.Subscribe;
|
import com.squareup.otto.Subscribe;
|
||||||
import jscl.JsclArithmeticException;
|
import jscl.*;
|
||||||
import jscl.MathEngine;
|
|
||||||
import jscl.NumeralBase;
|
|
||||||
import jscl.NumeralBaseException;
|
|
||||||
import jscl.math.Generic;
|
import jscl.math.Generic;
|
||||||
|
import jscl.math.function.Constants;
|
||||||
import jscl.math.function.IConstant;
|
import jscl.math.function.IConstant;
|
||||||
import jscl.text.ParseInterruptedException;
|
import jscl.text.ParseInterruptedException;
|
||||||
import org.solovyev.android.calculator.calculations.CalculationCancelledEvent;
|
import org.solovyev.android.calculator.calculations.CalculationCancelledEvent;
|
||||||
import org.solovyev.android.calculator.calculations.CalculationFailedEvent;
|
import org.solovyev.android.calculator.calculations.CalculationFailedEvent;
|
||||||
import org.solovyev.android.calculator.calculations.CalculationFinishedEvent;
|
import org.solovyev.android.calculator.calculations.CalculationFinishedEvent;
|
||||||
import org.solovyev.android.calculator.errors.FixableErrorsActivity;
|
|
||||||
import org.solovyev.android.calculator.functions.FunctionsRegistry;
|
import org.solovyev.android.calculator.functions.FunctionsRegistry;
|
||||||
import org.solovyev.android.calculator.jscl.JsclOperation;
|
import org.solovyev.android.calculator.jscl.JsclOperation;
|
||||||
import org.solovyev.android.calculator.units.CalculatorNumeralBase;
|
import org.solovyev.android.calculator.units.CalculatorNumeralBase;
|
||||||
@ -57,6 +54,7 @@ import javax.inject.Inject;
|
|||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@ -65,12 +63,13 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class Calculator implements CalculatorEventListener, SharedPreferences.OnSharedPreferenceChangeListener {
|
public class Calculator implements CalculatorEventListener, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
|
|
||||||
|
public static final long NO_SEQUENCE = -1;
|
||||||
private static final long PREFERENCE_CHECK_INTERVAL = TimeUnit.MINUTES.toMillis(15);
|
private static final long PREFERENCE_CHECK_INTERVAL = TimeUnit.MINUTES.toMillis(15);
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private final CalculatorEventContainer calculatorEventContainer = new ListCalculatorEventContainer();
|
private final CalculatorEventContainer calculatorEventContainer = new ListCalculatorEventContainer();
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private final AtomicLong counter = new AtomicLong(CalculatorUtils.FIRST_ID);
|
private static final AtomicLong SEQUENCER = new AtomicLong(NO_SEQUENCE);
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private final ToJsclTextProcessor preprocessor = ToJsclTextProcessor.getInstance();
|
private final ToJsclTextProcessor preprocessor = ToJsclTextProcessor.getInstance();
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@ -90,6 +89,8 @@ public class Calculator implements CalculatorEventListener, SharedPreferences.On
|
|||||||
PreferredPreferences preferredPreferences;
|
PreferredPreferences preferredPreferences;
|
||||||
@Inject
|
@Inject
|
||||||
Editor editor;
|
Editor editor;
|
||||||
|
@Inject
|
||||||
|
JsclMathEngine mathEngine;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public Calculator(@Nonnull SharedPreferences preferences, @Nonnull Bus bus, @Named(AppModule.THREAD_UI) @Nonnull Executor ui, @Named(AppModule.THREAD_BACKGROUND) @Nonnull Executor background) {
|
public Calculator(@Nonnull SharedPreferences preferences, @Nonnull Bus bus, @Named(AppModule.THREAD_UI) @Nonnull Executor ui, @Named(AppModule.THREAD_BACKGROUND) @Nonnull Executor background) {
|
||||||
@ -113,7 +114,7 @@ public class Calculator implements CalculatorEventListener, SharedPreferences.On
|
|||||||
String fromString = generic.toString();
|
String fromString = generic.toString();
|
||||||
if (!Strings.isEmpty(fromString)) {
|
if (!Strings.isEmpty(fromString)) {
|
||||||
try {
|
try {
|
||||||
fromString = ToJsclTextProcessor.getInstance().process(fromString).getExpression();
|
fromString = ToJsclTextProcessor.getInstance().process(fromString).getValue();
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
// ok, problems while processing occurred
|
// ok, problems while processing occurred
|
||||||
}
|
}
|
||||||
@ -130,19 +131,19 @@ public class Calculator implements CalculatorEventListener, SharedPreferences.On
|
|||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private CalculatorEventData nextEventData() {
|
private CalculatorEventData nextEventData() {
|
||||||
long eventId = counter.incrementAndGet();
|
final long eventId = nextSequence();
|
||||||
return CalculatorEventDataImpl.newInstance(eventId, eventId);
|
return CalculatorEventDataImpl.newInstance(eventId, eventId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private CalculatorEventData nextEventData(@Nonnull Object source) {
|
private CalculatorEventData nextEventData(@Nonnull Object source) {
|
||||||
long eventId = counter.incrementAndGet();
|
long eventId = nextSequence();
|
||||||
return CalculatorEventDataImpl.newInstance(eventId, eventId, source);
|
return CalculatorEventDataImpl.newInstance(eventId, eventId, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private CalculatorEventData nextEventData(@Nonnull Long sequenceId) {
|
private CalculatorEventData nextEventData(@Nonnull Long sequenceId) {
|
||||||
long eventId = counter.incrementAndGet();
|
long eventId = nextSequence();
|
||||||
return CalculatorEventDataImpl.newInstance(eventId, sequenceId);
|
return CalculatorEventDataImpl.newInstance(eventId, sequenceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +168,7 @@ public class Calculator implements CalculatorEventListener, SharedPreferences.On
|
|||||||
background.execute(new Runnable() {
|
background.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Calculator.this.evaluateAsync(eventDataId.getSequenceId(), operation, expression, null);
|
evaluateAsync(eventDataId.getSequenceId(), operation, expression);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -181,7 +182,7 @@ public class Calculator implements CalculatorEventListener, SharedPreferences.On
|
|||||||
background.execute(new Runnable() {
|
background.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
evaluateAsync(eventDataId.getSequenceId(), operation, expression, null);
|
evaluateAsync(eventDataId.getSequenceId(), operation, expression);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -215,72 +216,75 @@ public class Calculator implements CalculatorEventListener, SharedPreferences.On
|
|||||||
return CalculatorConversionEventDataImpl.newInstance(nextEventData(sequenceId), value, from, to, displayViewState);
|
return CalculatorConversionEventDataImpl.newInstance(nextEventData(sequenceId), value, from, to, displayViewState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void evaluateAsync(long sequence, @Nonnull JsclOperation o, @Nonnull String e) {
|
||||||
|
evaluateAsync(sequence, o, e, new ListMessageRegistry());
|
||||||
|
}
|
||||||
|
|
||||||
private void evaluateAsync(long sequence,
|
private void evaluateAsync(long sequence,
|
||||||
@Nonnull JsclOperation operation,
|
@Nonnull JsclOperation o,
|
||||||
@Nonnull String expression,
|
@Nonnull String e,
|
||||||
@Nullable MessageRegistry mr) {
|
@Nonnull MessageRegistry mr) {
|
||||||
|
e = e.trim();
|
||||||
|
if (Strings.isEmpty(e)) {
|
||||||
|
bus.post(new CalculationFinishedEvent(o, e, sequence));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
checkPreferredPreferences();
|
checkPreferredPreferences();
|
||||||
expression = expression.trim();
|
PreparedExpression pe = null;
|
||||||
|
|
||||||
PreparedExpression preparedExpression = null;
|
|
||||||
try {
|
try {
|
||||||
if (Strings.isEmpty(expression)) {
|
pe = prepare(e);
|
||||||
bus.post(new CalculationFinishedEvent(operation, expression, sequence));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
preparedExpression = prepareExpression(expression);
|
|
||||||
|
|
||||||
final String jsclExpression = preparedExpression.toString();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final MathEngine mathEngine = Locator.getInstance().getEngine().getMathEngine();
|
Locator.getInstance().getEngine().getMathEngine().setMessageRegistry(mr);
|
||||||
|
|
||||||
final MessageRegistry messageRegistry = new ListMessageRegistry();
|
final Generic result = o.evaluateGeneric(pe.value, mathEngine);
|
||||||
Locator.getInstance().getEngine().getMathEngine().setMessageRegistry(messageRegistry);
|
|
||||||
|
|
||||||
final Generic result = operation.evaluateGeneric(jsclExpression, mathEngine);
|
|
||||||
|
|
||||||
// NOTE: toString() method must be called here as ArithmeticOperationException may occur in it (just to avoid later check!)
|
// NOTE: toString() method must be called here as ArithmeticOperationException may occur in it (just to avoid later check!)
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
result.toString();
|
result.toString();
|
||||||
|
|
||||||
if (messageRegistry.hasMessage()) {
|
final String stringResult = o.getFromProcessor().process(result);
|
||||||
try {
|
bus.post(new CalculationFinishedEvent(o, e, sequence, result, stringResult, collectMessages(mr)));
|
||||||
final List<Message> messages = new ArrayList<>();
|
|
||||||
while (messageRegistry.hasMessage()) {
|
|
||||||
messages.add(messageRegistry.getMessage());
|
|
||||||
}
|
|
||||||
if (!messages.isEmpty()) {
|
|
||||||
fireCalculatorEvent(newCalculationEventData(operation, expression, sequence), CalculatorEventType.calculation_messages, messages);
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
// todo serso: not good but we need proper synchronization
|
|
||||||
Log.e("Calculator", e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final String stringResult = operation.getFromProcessor().process(result);
|
} catch (JsclArithmeticException exception) {
|
||||||
bus.post(new CalculationFinishedEvent(operation, expression, sequence, result, stringResult));
|
if (o == JsclOperation.numeric && exception.getCause() instanceof NumeralBaseException) {
|
||||||
|
evaluateAsync(sequence, JsclOperation.simplify, e, mr);
|
||||||
} catch (JsclArithmeticException e) {
|
|
||||||
if (operation == JsclOperation.numeric && e.getCause() instanceof NumeralBaseException) {
|
|
||||||
evaluateAsync(sequence, JsclOperation.simplify, expression, mr);
|
|
||||||
} else {
|
} else {
|
||||||
bus.post(new CalculationFailedEvent(operation, expression, sequence, e));
|
bus.post(new CalculationFailedEvent(o, e, sequence, exception));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (ArithmeticException e) {
|
} catch (ArithmeticException exception) {
|
||||||
handleException(sequence, operation, expression, mr, preparedExpression, new ParseException(expression, new CalculatorMessage(CalculatorMessages.msg_001, MessageType.error, e.getMessage())));
|
onException(sequence, o, e, mr, pe, new ParseException(e, new CalculatorMessage(CalculatorMessages.msg_001, MessageType.error, exception.getMessage())));
|
||||||
} catch (StackOverflowError e) {
|
} catch (StackOverflowError exception) {
|
||||||
handleException(sequence, operation, expression, mr, preparedExpression, new ParseException(expression, new CalculatorMessage(CalculatorMessages.msg_002, MessageType.error)));
|
onException(sequence, o, e, mr, pe, new ParseException(e, new CalculatorMessage(CalculatorMessages.msg_002, MessageType.error)));
|
||||||
} catch (jscl.text.ParseException e) {
|
} catch (jscl.text.ParseException exception) {
|
||||||
handleException(sequence, operation, expression, mr, preparedExpression, new ParseException(e));
|
onException(sequence, o, e, mr, pe, new ParseException(exception));
|
||||||
} catch (ParseInterruptedException e) {
|
} catch (ParseInterruptedException exception) {
|
||||||
bus.post(new CalculationCancelledEvent(operation, expression, sequence));
|
bus.post(new CalculationCancelledEvent(o, e, sequence));
|
||||||
} catch (ParseException e) {
|
} catch (ParseException exception) {
|
||||||
handleException(sequence, operation, expression, mr, preparedExpression, e);
|
onException(sequence, o, e, mr, pe, exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private List<Message> collectMessages(@Nonnull MessageRegistry mr) {
|
||||||
|
if (mr.hasMessage()) {
|
||||||
|
try {
|
||||||
|
final List<Message> messages = new ArrayList<>();
|
||||||
|
while (mr.hasMessage()) {
|
||||||
|
messages.add(mr.getMessage());
|
||||||
|
}
|
||||||
|
return messages;
|
||||||
|
} catch (Throwable exception) {
|
||||||
|
// several threads might use the same instance of MessageRegistry, as no proper synchronization is done
|
||||||
|
// catch Throwable here
|
||||||
|
Log.e("Calculator", exception.getMessage(), exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
private void checkPreferredPreferences() {
|
private void checkPreferredPreferences() {
|
||||||
if (shouldCheckPreferredPreferences()) {
|
if (shouldCheckPreferredPreferences()) {
|
||||||
preferredPreferences.check(false);
|
preferredPreferences.check(false);
|
||||||
@ -298,30 +302,23 @@ public class Calculator implements CalculatorEventListener, SharedPreferences.On
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public PreparedExpression prepareExpression(@Nonnull String expression) throws ParseException {
|
public PreparedExpression prepare(@Nonnull String expression) throws ParseException {
|
||||||
return preprocessor.process(expression);
|
return preprocessor.process(expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
private void onException(long sequence,
|
||||||
private CalculatorEventData newCalculationEventData(@Nonnull JsclOperation operation,
|
@Nonnull JsclOperation operation,
|
||||||
@Nonnull String expression,
|
@Nonnull String e,
|
||||||
@Nonnull Long calculationId) {
|
@Nonnull MessageRegistry mr,
|
||||||
return new CalculatorEvaluationEventDataImpl(nextEventData(calculationId), operation, expression);
|
@Nullable PreparedExpression pe,
|
||||||
}
|
@Nonnull ParseException parseException) {
|
||||||
|
|
||||||
private void handleException(long sequence,
|
|
||||||
@Nonnull JsclOperation operation,
|
|
||||||
@Nonnull String expression,
|
|
||||||
@Nullable MessageRegistry mr,
|
|
||||||
@Nullable PreparedExpression preparedExpression,
|
|
||||||
@Nonnull ParseException parseException) {
|
|
||||||
if (operation == JsclOperation.numeric
|
if (operation == JsclOperation.numeric
|
||||||
&& preparedExpression != null
|
&& pe != null
|
||||||
&& preparedExpression.isExistsUndefinedVar()) {
|
&& pe.hasUndefinedVariables()) {
|
||||||
evaluateAsync(sequence, JsclOperation.simplify, expression, mr);
|
evaluateAsync(sequence, JsclOperation.simplify, e, mr);
|
||||||
} else {
|
return;
|
||||||
bus.post(new CalculationFailedEvent(operation, expression, sequence, parseException));
|
|
||||||
}
|
}
|
||||||
|
bus.post(new CalculationFailedEvent(operation, e, sequence, parseException));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@ -423,9 +420,9 @@ public class Calculator implements CalculatorEventListener, SharedPreferences.On
|
|||||||
|
|
||||||
private void updateAnsVariable(@NonNull String value) {
|
private void updateAnsVariable(@NonNull String value) {
|
||||||
final VariablesRegistry variablesRegistry = Locator.getInstance().getEngine().getVariablesRegistry();
|
final VariablesRegistry variablesRegistry = Locator.getInstance().getEngine().getVariablesRegistry();
|
||||||
final IConstant variable = variablesRegistry.get(VariablesRegistry.ANS);
|
final IConstant variable = variablesRegistry.get(Constants.ANS);
|
||||||
|
|
||||||
final CppVariable.Builder b = variable != null ? CppVariable.builder(variable) : CppVariable.builder(VariablesRegistry.ANS);
|
final CppVariable.Builder b = variable != null ? CppVariable.builder(variable) : CppVariable.builder(Constants.ANS);
|
||||||
b.withValue(value);
|
b.withValue(value);
|
||||||
b.withSystem(true);
|
b.withSystem(true);
|
||||||
b.withDescription(CalculatorMessages.getBundle().getString(CalculatorMessages.ans_description));
|
b.withDescription(CalculatorMessages.getBundle().getString(CalculatorMessages.ans_description));
|
||||||
@ -460,7 +457,7 @@ public class Calculator implements CalculatorEventListener, SharedPreferences.On
|
|||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void onVariableChanged(@NonNull VariablesRegistry.ChangedEvent e) {
|
public void onVariableChanged(@NonNull VariablesRegistry.ChangedEvent e) {
|
||||||
if (!e.newVariable.getName().equals(VariablesRegistry.ANS)) {
|
if (!e.newVariable.getName().equals(Constants.ANS)) {
|
||||||
evaluate();
|
evaluate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -468,9 +465,6 @@ public class Calculator implements CalculatorEventListener, SharedPreferences.On
|
|||||||
@Override
|
@Override
|
||||||
public void onCalculatorEvent(@Nonnull CalculatorEventData calculatorEventData, @Nonnull CalculatorEventType calculatorEventType, @Nullable Object data) {
|
public void onCalculatorEvent(@Nonnull CalculatorEventData calculatorEventData, @Nonnull CalculatorEventType calculatorEventType, @Nullable Object data) {
|
||||||
switch (calculatorEventType) {
|
switch (calculatorEventType) {
|
||||||
case calculation_messages:
|
|
||||||
FixableErrorsActivity.show(App.getApplication(), (List<Message>) data);
|
|
||||||
break;
|
|
||||||
case show_history:
|
case show_history:
|
||||||
ActivityLauncher.showHistory(App.getApplication());
|
ActivityLauncher.showHistory(App.getApplication());
|
||||||
break;
|
break;
|
||||||
@ -520,4 +514,7 @@ public class Calculator implements CalculatorEventListener, SharedPreferences.On
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static long nextSequence() {
|
||||||
|
return SEQUENCER.incrementAndGet();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,15 +22,14 @@
|
|||||||
|
|
||||||
package org.solovyev.android.calculator;
|
package org.solovyev.android.calculator;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
|
|
||||||
import jscl.math.Generic;
|
import jscl.math.Generic;
|
||||||
import jscl.math.function.Constant;
|
import jscl.math.function.Constant;
|
||||||
import jscl.math.function.IConstant;
|
import jscl.math.function.IConstant;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User: serso
|
* User: serso
|
||||||
* Date: 9/22/12
|
* Date: 9/22/12
|
||||||
@ -38,15 +37,13 @@ import jscl.math.function.IConstant;
|
|||||||
*/
|
*/
|
||||||
public final class CalculatorUtils {
|
public final class CalculatorUtils {
|
||||||
|
|
||||||
static final long FIRST_ID = 0;
|
|
||||||
|
|
||||||
private CalculatorUtils() {
|
private CalculatorUtils() {
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public static CalculatorEventData createFirstEventDataId() {
|
public static CalculatorEventData createFirstEventDataId() {
|
||||||
return CalculatorEventDataImpl.newInstance(FIRST_ID, FIRST_ID);
|
return CalculatorEventDataImpl.newInstance(Calculator.NO_SEQUENCE, Calculator.NO_SEQUENCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
|
|
||||||
package org.solovyev.android.calculator;
|
package org.solovyev.android.calculator;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.content.Context;
|
||||||
import android.view.ContextMenu;
|
import android.view.ContextMenu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -33,6 +35,7 @@ import org.solovyev.android.Check;
|
|||||||
import org.solovyev.android.calculator.calculations.CalculationCancelledEvent;
|
import org.solovyev.android.calculator.calculations.CalculationCancelledEvent;
|
||||||
import org.solovyev.android.calculator.calculations.CalculationFailedEvent;
|
import org.solovyev.android.calculator.calculations.CalculationFailedEvent;
|
||||||
import org.solovyev.android.calculator.calculations.CalculationFinishedEvent;
|
import org.solovyev.android.calculator.calculations.CalculationFinishedEvent;
|
||||||
|
import org.solovyev.android.calculator.errors.FixableErrorsActivity;
|
||||||
import org.solovyev.android.calculator.jscl.JsclOperation;
|
import org.solovyev.android.calculator.jscl.JsclOperation;
|
||||||
import org.solovyev.android.calculator.view.NumeralBaseConverterDialog;
|
import org.solovyev.android.calculator.view.NumeralBaseConverterDialog;
|
||||||
|
|
||||||
@ -53,6 +56,8 @@ public class Display implements CalculatorEventListener, View.OnClickListener, V
|
|||||||
@Nonnull
|
@Nonnull
|
||||||
private final Bus bus;
|
private final Bus bus;
|
||||||
@Inject
|
@Inject
|
||||||
|
Application application;
|
||||||
|
@Inject
|
||||||
Lazy<Keyboard> keyboard;
|
Lazy<Keyboard> keyboard;
|
||||||
@Inject
|
@Inject
|
||||||
Lazy<Clipboard> clipboard;
|
Lazy<Clipboard> clipboard;
|
||||||
@ -84,6 +89,10 @@ public class Display implements CalculatorEventListener, View.OnClickListener, V
|
|||||||
public void onCalculationFinished(@Nonnull CalculationFinishedEvent e) {
|
public void onCalculationFinished(@Nonnull CalculationFinishedEvent e) {
|
||||||
if (e.sequence < state.sequence) return;
|
if (e.sequence < state.sequence) return;
|
||||||
setState(DisplayState.createValid(e.operation, e.result, e.stringResult, e.sequence));
|
setState(DisplayState.createValid(e.operation, e.result, e.stringResult, e.sequence));
|
||||||
|
if (!e.messages.isEmpty()) {
|
||||||
|
final Context context = view != null ? view.getContext() : application;
|
||||||
|
FixableErrorsActivity.show(context, e.messages);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
|
@ -64,18 +64,18 @@ public class DisplayState implements Parcelable, ContextMenu.ContextMenuInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DisplayState(@Nonnull JSONObject json) {
|
DisplayState(@Nonnull JSONObject json) {
|
||||||
this(json.optString(JSON_TEXT), true, EditorState.NO_SEQUENCE);
|
this(json.optString(JSON_TEXT), true, Calculator.NO_SEQUENCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DisplayState(Parcel in) {
|
private DisplayState(Parcel in) {
|
||||||
text = in.readString();
|
text = in.readString();
|
||||||
valid = in.readByte() != 0;
|
valid = in.readByte() != 0;
|
||||||
sequence = EditorState.NO_SEQUENCE;
|
sequence = Calculator.NO_SEQUENCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public static DisplayState empty() {
|
public static DisplayState empty() {
|
||||||
return new DisplayState("", true, EditorState.NO_SEQUENCE);
|
return new DisplayState("", true, Calculator.NO_SEQUENCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
|
@ -25,18 +25,14 @@ package org.solovyev.android.calculator;
|
|||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public class EditorState implements Parcelable {
|
public class EditorState implements Parcelable {
|
||||||
|
|
||||||
public static final long NO_SEQUENCE = -1;
|
|
||||||
public static final Creator<EditorState> CREATOR = new Creator<EditorState>() {
|
public static final Creator<EditorState> CREATOR = new Creator<EditorState>() {
|
||||||
@Override
|
@Override
|
||||||
public EditorState createFromParcel(Parcel in) {
|
public EditorState createFromParcel(Parcel in) {
|
||||||
@ -50,7 +46,6 @@ public class EditorState implements Parcelable {
|
|||||||
};
|
};
|
||||||
private static final String JSON_TEXT = "t";
|
private static final String JSON_TEXT = "t";
|
||||||
private static final String JSON_SELECTION = "s";
|
private static final String JSON_SELECTION = "s";
|
||||||
private static AtomicLong counter = new AtomicLong(NO_SEQUENCE + 1);
|
|
||||||
public final long sequence;
|
public final long sequence;
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public final CharSequence text;
|
public final CharSequence text;
|
||||||
@ -63,7 +58,7 @@ public class EditorState implements Parcelable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private EditorState(@Nonnull CharSequence text, int selection) {
|
private EditorState(@Nonnull CharSequence text, int selection) {
|
||||||
this.sequence = counter.getAndIncrement();
|
this.sequence = Calculator.nextSequence();
|
||||||
this.text = text;
|
this.text = text;
|
||||||
this.selection = selection;
|
this.selection = selection;
|
||||||
}
|
}
|
||||||
@ -73,7 +68,7 @@ public class EditorState implements Parcelable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private EditorState(Parcel in) {
|
private EditorState(Parcel in) {
|
||||||
sequence = NO_SEQUENCE;
|
sequence = Calculator.NO_SEQUENCE;
|
||||||
selection = in.readInt();
|
selection = in.readInt();
|
||||||
textString = in.readString();
|
textString = in.readString();
|
||||||
text = textString;
|
text = textString;
|
||||||
|
@ -31,48 +31,43 @@ import java.util.List;
|
|||||||
public class PreparedExpression implements CharSequence {
|
public class PreparedExpression implements CharSequence {
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private String expression;
|
public final String value;
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private List<IConstant> undefinedVars;
|
public final List<IConstant> undefinedVariables;
|
||||||
|
|
||||||
public PreparedExpression(@Nonnull String expression, @Nonnull List<IConstant> undefinedVars) {
|
public PreparedExpression(@Nonnull String value, @Nonnull List<IConstant> undefinedVariables) {
|
||||||
this.expression = expression;
|
this.value = value;
|
||||||
this.undefinedVars = undefinedVars;
|
this.undefinedVariables = undefinedVariables;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public String getExpression() {
|
public String getValue() {
|
||||||
return expression;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isExistsUndefinedVar() {
|
public boolean hasUndefinedVariables() {
|
||||||
return !this.undefinedVars.isEmpty();
|
return !undefinedVariables.isEmpty();
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
public List<IConstant> getUndefinedVars() {
|
|
||||||
return undefinedVars;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int length() {
|
public int length() {
|
||||||
return expression.length();
|
return value.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public char charAt(int i) {
|
public char charAt(int i) {
|
||||||
return expression.charAt(i);
|
return value.charAt(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CharSequence subSequence(int i, int i1) {
|
public CharSequence subSequence(int i, int i1) {
|
||||||
return expression.subSequence(i, i1);
|
return value.subSequence(i, i1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return this.expression;
|
return this.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,9 +52,6 @@ import java.util.Map;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class VariablesRegistry extends BaseEntitiesRegistry<IConstant> {
|
public class VariablesRegistry extends BaseEntitiesRegistry<IConstant> {
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
public static final String ANS = "ans";
|
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private static final Map<String, String> substitutes = new HashMap<>();
|
private static final Map<String, String> substitutes = new HashMap<>();
|
||||||
|
|
||||||
|
@ -2,25 +2,32 @@ package org.solovyev.android.calculator.calculations;
|
|||||||
|
|
||||||
import jscl.math.Generic;
|
import jscl.math.Generic;
|
||||||
import org.solovyev.android.calculator.jscl.JsclOperation;
|
import org.solovyev.android.calculator.jscl.JsclOperation;
|
||||||
|
import org.solovyev.common.msg.Message;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public final class CalculationFinishedEvent extends BaseCalculationEvent {
|
public final class CalculationFinishedEvent extends BaseCalculationEvent {
|
||||||
@Nullable
|
@Nullable
|
||||||
public final Generic result;
|
public final Generic result;
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public final String stringResult;
|
public final String stringResult;
|
||||||
|
@Nonnull
|
||||||
|
public final List<Message> messages;
|
||||||
|
|
||||||
public CalculationFinishedEvent(@Nonnull JsclOperation operation, @Nonnull String expression, long sequence) {
|
public CalculationFinishedEvent(@Nonnull JsclOperation operation, @Nonnull String expression, long sequence) {
|
||||||
super(operation, expression, sequence);
|
super(operation, expression, sequence);
|
||||||
result = null;
|
result = null;
|
||||||
stringResult = "";
|
stringResult = "";
|
||||||
|
messages = Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public CalculationFinishedEvent(@Nonnull JsclOperation operation, @Nonnull String expression, long sequence, @Nullable Generic result, @Nonnull String stringResult) {
|
public CalculationFinishedEvent(@Nonnull JsclOperation operation, @Nonnull String expression, long sequence, @Nullable Generic result, @Nonnull String stringResult, @Nonnull List<Message> messages) {
|
||||||
super(operation, expression, sequence);
|
super(operation, expression, sequence);
|
||||||
this.result = result;
|
this.result = result;
|
||||||
this.stringResult = stringResult;
|
this.stringResult = stringResult;
|
||||||
|
this.messages = messages;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -271,7 +271,7 @@ public class EditFunctionFragment extends BaseDialogFragment implements View.OnC
|
|||||||
|
|
||||||
private boolean applyData() {
|
private boolean applyData() {
|
||||||
try {
|
try {
|
||||||
final String body = calculator.prepareExpression(bodyView.getText().toString()).getExpression();
|
final String body = calculator.prepare(bodyView.getText().toString()).getValue();
|
||||||
|
|
||||||
final CppFunction newFunction = CppFunction.builder(nameView.getText().toString(), body)
|
final CppFunction newFunction = CppFunction.builder(nameView.getText().toString(), body)
|
||||||
.withId(isNewFunction() ? NO_ID : function.id)
|
.withId(isNewFunction() ? NO_ID : function.id)
|
||||||
@ -326,7 +326,7 @@ public class EditFunctionFragment extends BaseDialogFragment implements View.OnC
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
calculator.prepareExpression(body);
|
calculator.prepare(body);
|
||||||
clearError(bodyLabel);
|
clearError(bodyLabel);
|
||||||
return true;
|
return true;
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
|
@ -100,7 +100,7 @@ public class History {
|
|||||||
final OldDisplayHistoryState oldDisplay = state.getDisplayState();
|
final OldDisplayHistoryState oldDisplay = state.getDisplayState();
|
||||||
final String editorText = oldEditor.getText();
|
final String editorText = oldEditor.getText();
|
||||||
final EditorState editor = EditorState.create(Strings.nullToEmpty(editorText), oldEditor.getCursorPosition());
|
final EditorState editor = EditorState.create(Strings.nullToEmpty(editorText), oldEditor.getCursorPosition());
|
||||||
final DisplayState display = DisplayState.createValid(oldDisplay.getJsclOperation(), null, Strings.nullToEmpty(oldDisplay.getEditorState().getText()), EditorState.NO_SEQUENCE);
|
final DisplayState display = DisplayState.createValid(oldDisplay.getJsclOperation(), null, Strings.nullToEmpty(oldDisplay.getEditorState().getText()), Calculator.NO_SEQUENCE);
|
||||||
states.add(HistoryState.builder(editor, display).withTime(state.getTime()).withComment(state.getComment()).build());
|
states.add(HistoryState.builder(editor, display).withTime(state.getTime()).withComment(state.getComment()).build());
|
||||||
}
|
}
|
||||||
return states;
|
return states;
|
||||||
|
@ -61,9 +61,8 @@ public class VariablesFragment extends BaseEntitiesFragment<IConstant> {
|
|||||||
|
|
||||||
public static boolean isValidValue(@Nonnull String value) {
|
public static boolean isValidValue(@Nonnull String value) {
|
||||||
try {
|
try {
|
||||||
final PreparedExpression expression = ToJsclTextProcessor.getInstance().process(value);
|
final PreparedExpression pe = ToJsclTextProcessor.getInstance().process(value);
|
||||||
final List<IConstant> variables = expression.getUndefinedVars();
|
return !pe.hasUndefinedVariables();
|
||||||
return variables.isEmpty();
|
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ public class NumeralBaseConverterDialog {
|
|||||||
if (!Strings.isEmpty(initialFromValue)) {
|
if (!Strings.isEmpty(initialFromValue)) {
|
||||||
String value = initialFromValue;
|
String value = initialFromValue;
|
||||||
try {
|
try {
|
||||||
value = ToJsclTextProcessor.getInstance().process(value).getExpression();
|
value = ToJsclTextProcessor.getInstance().process(value).getValue();
|
||||||
b.setFromValue(UnitImpl.newInstance(value, CalculatorNumeralBase.valueOf(Locator.getInstance().getEngine().getMathEngine().getNumeralBase())));
|
b.setFromValue(UnitImpl.newInstance(value, CalculatorNumeralBase.valueOf(Locator.getInstance().getEngine().getMathEngine().getNumeralBase())));
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
b.setFromValue(UnitImpl.newInstance(value, CalculatorNumeralBase.valueOf(Locator.getInstance().getEngine().getMathEngine().getNumeralBase())));
|
b.setFromValue(UnitImpl.newInstance(value, CalculatorNumeralBase.valueOf(Locator.getInstance().getEngine().getMathEngine().getNumeralBase())));
|
||||||
|
@ -90,7 +90,7 @@ public class AndroidEngineTest extends AbstractCalculatorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testI() throws ParseException, EvalException {
|
public void testI() throws ParseException {
|
||||||
final MathEngine cm = Locator.getInstance().getEngine().getMathEngine();
|
final MathEngine cm = Locator.getInstance().getEngine().getMathEngine();
|
||||||
|
|
||||||
CalculatorTestUtils.assertEval("-i", cm.evaluate("i^3"));
|
CalculatorTestUtils.assertEval("-i", cm.evaluate("i^3"));
|
||||||
|
@ -22,26 +22,24 @@
|
|||||||
|
|
||||||
package org.solovyev.android.calculator.model;
|
package org.solovyev.android.calculator.model;
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.BeforeClass;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.solovyev.android.calculator.AbstractCalculatorTest;
|
|
||||||
import org.solovyev.android.calculator.ParseException;
|
|
||||||
import org.solovyev.android.calculator.CalculatorTestUtils;
|
|
||||||
import org.solovyev.android.calculator.Locator;
|
|
||||||
import org.solovyev.common.Converter;
|
|
||||||
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
|
|
||||||
import au.com.bytecode.opencsv.CSVReader;
|
import au.com.bytecode.opencsv.CSVReader;
|
||||||
import jscl.JsclMathEngine;
|
import jscl.JsclMathEngine;
|
||||||
import jscl.MathEngine;
|
import jscl.MathEngine;
|
||||||
import jscl.math.Expression;
|
import jscl.math.Expression;
|
||||||
import jscl.util.ExpressionGeneratorWithInput;
|
import jscl.util.ExpressionGeneratorWithInput;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.solovyev.android.calculator.AbstractCalculatorTest;
|
||||||
|
import org.solovyev.android.calculator.CalculatorTestUtils;
|
||||||
|
import org.solovyev.android.calculator.Locator;
|
||||||
|
import org.solovyev.android.calculator.ParseException;
|
||||||
|
import org.solovyev.common.Converter;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User: serso
|
* User: serso
|
||||||
@ -56,7 +54,7 @@ public class NumeralBaseTest extends AbstractCalculatorTest {
|
|||||||
Locator.getInstance().getEngine().getMathEngine().setPrecision(3);
|
Locator.getInstance().getEngine().getMathEngine().setPrecision(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void testExpression(@Nonnull String[] line, @Nonnull Converter<String, String> converter) throws jscl.text.ParseException, EvalException, ParseException {
|
public static void testExpression(@Nonnull String[] line, @Nonnull Converter<String, String> converter) throws jscl.text.ParseException, ParseException {
|
||||||
final String dec = line[0].toUpperCase();
|
final String dec = line[0].toUpperCase();
|
||||||
final String hex = "0x:" + line[1].toUpperCase();
|
final String hex = "0x:" + line[1].toUpperCase();
|
||||||
final String bin = "0b:" + line[2].toUpperCase();
|
final String bin = "0b:" + line[2].toUpperCase();
|
||||||
|
@ -149,7 +149,7 @@ public class ToJsclTextProcessorTest extends AbstractCalculatorTest {
|
|||||||
Assert.assertEquals("101", JsclMathEngine.getInstance().evaluate("10+11"));
|
Assert.assertEquals("101", JsclMathEngine.getInstance().evaluate("10+11"));
|
||||||
|
|
||||||
JsclMathEngine.getInstance().setNumeralBase(NumeralBase.hex);
|
JsclMathEngine.getInstance().setNumeralBase(NumeralBase.hex);
|
||||||
Assert.assertEquals("56CE+CAD", processor.process("56CE+CAD").getExpression());
|
Assert.assertEquals("56CE+CAD", processor.process("56CE+CAD").getValue());
|
||||||
} finally {
|
} finally {
|
||||||
JsclMathEngine.getInstance().setNumeralBase(defaultNumeralBase);
|
JsclMathEngine.getInstance().setNumeralBase(defaultNumeralBase);
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ import org.solovyev.common.msg.MessageRegistry;
|
|||||||
import org.solovyev.common.msg.Messages;
|
import org.solovyev.common.msg.Messages;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.text.DecimalFormatSymbols;
|
import java.text.DecimalFormatSymbols;
|
||||||
@ -168,19 +167,16 @@ public class JsclMathEngine implements MathEngine {
|
|||||||
|
|
||||||
// detect if current number is precisely equals to constant in constants' registry (NOTE: ONLY FOR SYSTEM CONSTANTS)
|
// detect if current number is precisely equals to constant in constants' registry (NOTE: ONLY FOR SYSTEM CONSTANTS)
|
||||||
final Double localValue = value;
|
final Double localValue = value;
|
||||||
IConstant constant = Collections.find(this.getConstantsRegistry().getSystemEntities(), new JPredicate<IConstant>() {
|
IConstant constant = Collections.find(getConstantsRegistry().getSystemEntities(), new JPredicate<IConstant>() {
|
||||||
public boolean apply(@Nullable IConstant constant) {
|
public boolean apply(@Nonnull IConstant constant) {
|
||||||
if (constant != null) {
|
if (!localValue.equals(constant.getDoubleValue())) {
|
||||||
if (localValue.equals(constant.getDoubleValue())) {
|
return false;
|
||||||
if (!constant.getName().equals(Constants.PI_INV.getName())) {
|
|
||||||
if (!constant.getName().equals(Constants.PI.getName()) || JsclMathEngine.getInstance().getAngleUnits() == AngleUnit.rad) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
final String name = constant.getName();
|
||||||
return false;
|
if (name.equals(Constants.PI_INV.getName()) || name.equals(Constants.ANS)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !name.equals(Constants.PI.getName()) || JsclMathEngine.getInstance().getAngleUnits() == AngleUnit.rad;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@ package jscl.math.function;
|
|||||||
|
|
||||||
import jscl.math.JsclInteger;
|
import jscl.math.JsclInteger;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User: serso
|
* User: serso
|
||||||
* Date: 1/7/12
|
* Date: 1/7/12
|
||||||
@ -14,6 +16,8 @@ public final class Constants {
|
|||||||
public static final Constant I = new Constant("i");
|
public static final Constant I = new Constant("i");
|
||||||
public static final Constant INF = new Constant("∞");
|
public static final Constant INF = new Constant("∞");
|
||||||
public static final Constant INF_2 = new Constant("Infinity");
|
public static final Constant INF_2 = new Constant("Infinity");
|
||||||
|
@Nonnull
|
||||||
|
public static final String ANS = "ans";
|
||||||
|
|
||||||
// not intended for instantiation
|
// not intended for instantiation
|
||||||
private Constants() {
|
private Constants() {
|
||||||
|
Loading…
Reference in New Issue
Block a user