New core implementation

This commit is contained in:
serso
2012-09-20 18:35:11 +04:00
parent eb37fe495b
commit 1d2aaa9d47
40 changed files with 1845 additions and 1153 deletions

View File

@@ -32,7 +32,7 @@ import org.solovyev.android.AndroidUtils;
import org.solovyev.android.FontSizeAdjuster;
import org.solovyev.android.LocalBinder;
import org.solovyev.android.calculator.about.CalculatorReleaseNotesActivity;
import org.solovyev.android.calculator.history.CalculatorHistory;
import org.solovyev.android.calculator.history.AndroidCalculatorHistoryImpl;
import org.solovyev.android.calculator.history.CalculatorHistoryState;
import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.calculator.view.AngleUnitsButton;
@@ -136,7 +136,7 @@ public class CalculatorActivity extends Activity implements FontSizeAdjuster, Sh
vibrator = (Vibrator) this.getSystemService(VIBRATOR_SERVICE);
CalculatorHistory.instance.load(this, preferences);
AndroidCalculatorHistoryImpl.instance.load(this, preferences);
calculatorModel = CalculatorModel.instance.init(this, preferences, CalculatorEngine.instance);
dpclRegister.clear();

View File

@@ -19,7 +19,7 @@ import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.calculator.text.TextProcessor;
import org.solovyev.android.calculator.model.ToJsclTextProcessor;
import org.solovyev.android.calculator.ToJsclTextProcessor;
import org.solovyev.android.calculator.view.NumeralBaseConverterDialog;
import org.solovyev.android.calculator.view.TextHighlighter;
import org.solovyev.android.calculator.view.UnitConverterViewBuilder;
@@ -37,7 +37,7 @@ import java.util.Set;
* Date: 9/17/11
* Time: 10:58 PM
*/
public class CalculatorDisplay extends AutoResizeTextView implements ICalculatorDisplay{
public class CalculatorDisplay extends AutoResizeTextView implements JCalculatorDisplay {
private static enum ConversionMenuItem implements AMenuItem<CalculatorDisplay> {
convert_to_bin(NumeralBase.bin),

View File

@@ -20,13 +20,12 @@ import android.widget.Toast;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.CursorControl;
import org.solovyev.android.calculator.history.CalculatorHistory;
import org.solovyev.android.calculator.history.AndroidCalculatorHistoryImpl;
import org.solovyev.android.calculator.history.CalculatorHistoryState;
import org.solovyev.android.calculator.history.TextViewEditorAdapter;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.calculator.model.CalculatorEvalException;
import org.solovyev.android.history.HistoryControl;
import org.solovyev.android.menu.AMenuBuilder;
import org.solovyev.android.menu.MenuImpl;
@@ -70,7 +69,7 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
this.display = (CalculatorDisplay) activity.findViewById(R.id.calculatorDisplay);
this.display.setOnClickListener(new CalculatorDisplayOnClickListener(activity));
final CalculatorHistoryState lastState = CalculatorHistory.instance.getLastHistoryState();
final CalculatorHistoryState lastState = AndroidCalculatorHistoryImpl.instance.getLastHistoryState();
if (lastState == null) {
saveHistoryState();
} else {
@@ -110,7 +109,7 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
}
private void saveHistoryState() {
CalculatorHistory.instance.addState(getCurrentHistoryState());
AndroidCalculatorHistoryImpl.instance.addState(getCurrentHistoryState());
}
public void setCursorOnStart() {
@@ -196,14 +195,14 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
if (delayEvaluate) {
if (historyState == null) {
CalculatorHistory.instance.addState(localHistoryState);
AndroidCalculatorHistoryImpl.instance.addState(localHistoryState);
}
// todo serso: this is not correct - operation is processing still in the same thread
new Handler().postDelayed(pendingOperation.getObject(), EVAL_DELAY_MILLIS);
} else {
pendingOperation.getObject().run();
if (historyState == null) {
CalculatorHistory.instance.addState(localHistoryState);
AndroidCalculatorHistoryImpl.instance.addState(localHistoryState);
}
}
}
@@ -330,9 +329,9 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
@Override
public void doHistoryAction(@NotNull HistoryAction historyAction) {
synchronized (CalculatorHistory.instance) {
if (CalculatorHistory.instance.isActionAvailable(historyAction)) {
final CalculatorHistoryState newState = CalculatorHistory.instance.doAction(historyAction, getCurrentHistoryState());
synchronized (AndroidCalculatorHistoryImpl.instance) {
if (AndroidCalculatorHistoryImpl.instance.isActionAvailable(historyAction)) {
final CalculatorHistoryState newState = AndroidCalculatorHistoryImpl.instance.doAction(historyAction, getCurrentHistoryState());
if (newState != null) {
setCurrentHistoryState(newState);
}
@@ -342,7 +341,7 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
@Override
public void setCurrentHistoryState(@NotNull CalculatorHistoryState editorHistoryState) {
synchronized (CalculatorHistory.instance) {
synchronized (AndroidCalculatorHistoryImpl.instance) {
Log.d(this.getClass().getName(), "Saved history found: " + editorHistoryState);
editorHistoryState.setValuesFromHistory(new TextViewEditorAdapter(this.editor), this.display);
@@ -362,7 +361,7 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
@Override
@NotNull
public CalculatorHistoryState getCurrentHistoryState() {
synchronized (CalculatorHistory.instance) {
synchronized (AndroidCalculatorHistoryImpl.instance) {
return CalculatorHistoryState.newInstance(new TextViewEditorAdapter(this.editor), this.display);
}
}

View File

@@ -1,263 +1,263 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.history;
import android.app.ListActivity;
import android.content.Context;
import android.os.Bundle;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import com.google.ads.AdView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.ads.AdsController;
import org.solovyev.android.calculator.CalculatorModel;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.menu.AMenuBuilder;
import org.solovyev.android.menu.MenuImpl;
import org.solovyev.common.collections.CollectionsUtils;
import org.solovyev.common.equals.Equalizer;
import org.solovyev.common.filter.Filter;
import org.solovyev.common.filter.FilterRule;
import org.solovyev.common.filter.FilterRulesChain;
import org.solovyev.common.text.StringUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* User: serso
* Date: 10/15/11
* Time: 1:13 PM
*/
public abstract class AbstractHistoryActivity extends ListActivity {
public static final Comparator<CalculatorHistoryState> COMPARATOR = new Comparator<CalculatorHistoryState>() {
@Override
public int compare(CalculatorHistoryState state1, CalculatorHistoryState state2) {
if (state1.isSaved() == state2.isSaved()) {
long l = state2.getTime() - state1.getTime();
return l > 0l ? 1 : (l < 0l ? -1 : 0);
} else if (state1.isSaved()) {
return -1;
} else if (state2.isSaved()) {
return 1;
}
return 0;
}
};
@NotNull
private ArrayAdapter<CalculatorHistoryState> adapter;
@Nullable
private AdView adView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.history_activity);
adView = AdsController.getInstance().inflateAd(this);
adapter = new HistoryArrayAdapter(this, getLayoutId(), R.id.history_item, new ArrayList<CalculatorHistoryState>());
setListAdapter(adapter);
final ListView lv = getListView();
lv.setTextFilterEnabled(true);
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(final AdapterView<?> parent,
final View view,
final int position,
final long id) {
useHistoryItem((CalculatorHistoryState) parent.getItemAtPosition(position), AbstractHistoryActivity.this);
}
});
lv.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, final int position, long id) {
final CalculatorHistoryState historyState = (CalculatorHistoryState) parent.getItemAtPosition(position);
final Context context = AbstractHistoryActivity.this;
final HistoryItemMenuData data = new HistoryItemMenuData(historyState, adapter);
final List<HistoryItemMenuItem> menuItems = CollectionsUtils.asList(HistoryItemMenuItem.values());
if (historyState.isSaved()) {
menuItems.remove(HistoryItemMenuItem.save);
} else {
if (isAlreadySaved(historyState)) {
menuItems.remove(HistoryItemMenuItem.save);
}
menuItems.remove(HistoryItemMenuItem.remove);
menuItems.remove(HistoryItemMenuItem.edit);
}
if (historyState.getDisplayState().isValid() && StringUtils.isEmpty(historyState.getDisplayState().getEditorState().getText())) {
menuItems.remove(HistoryItemMenuItem.copy_result);
}
final AMenuBuilder<HistoryItemMenuItem, HistoryItemMenuData> menuBuilder = AMenuBuilder.newInstance(context, MenuImpl.newInstance(menuItems));
menuBuilder.create(data).show();
return true;
}
});
}
@Override
protected void onDestroy() {
if ( this.adView != null ) {
this.adView.destroy();
}
super.onDestroy();
}
protected abstract int getLayoutId();
@Override
protected void onResume() {
super.onResume();
final List<CalculatorHistoryState> historyList = getHistoryList();
try {
this.adapter.setNotifyOnChange(false);
this.adapter.clear();
for (CalculatorHistoryState historyState : historyList) {
this.adapter.add(historyState);
}
} finally {
this.adapter.setNotifyOnChange(true);
}
this.adapter.notifyDataSetChanged();
}
public static boolean isAlreadySaved(@NotNull CalculatorHistoryState historyState) {
assert !historyState.isSaved();
boolean result = false;
try {
historyState.setSaved(true);
if ( CollectionsUtils.contains(historyState, CalculatorHistory.instance.getSavedHistory(), new Equalizer<CalculatorHistoryState>() {
@Override
public boolean equals(@Nullable CalculatorHistoryState first, @Nullable CalculatorHistoryState second) {
return first != null && second != null &&
first.getTime() == second.getTime() &&
first.getDisplayState().equals(second.getDisplayState()) &&
first.getEditorState().equals(second.getEditorState());
}
}) ) {
result = true;
}
} finally {
historyState.setSaved(false);
}
return result;
}
public static void useHistoryItem(@NotNull final CalculatorHistoryState historyState, @NotNull AbstractHistoryActivity activity) {
// before evaluating history item - clear display (in order to get Error message in display if evaluation fail)
CalculatorModel.instance.getDisplay().setText("");
CalculatorModel.instance.doTextOperation(new CalculatorModel.TextOperation() {
@Override
public void doOperation(@NotNull EditText editor) {
final EditorHistoryState editorState = historyState.getEditorState();
editor.setText(editorState.getText());
editor.setSelection(editorState.getCursorPosition());
}
}, false, historyState.getDisplayState().getJsclOperation(), true);
CalculatorModel.instance.setCursorOnEnd();
activity.finish();
}
@NotNull
private List<CalculatorHistoryState> getHistoryList() {
final List<CalculatorHistoryState> calculatorHistoryStates = getHistoryItems();
Collections.sort(calculatorHistoryStates, COMPARATOR);
final FilterRulesChain<CalculatorHistoryState> filterRulesChain = new FilterRulesChain<CalculatorHistoryState>();
filterRulesChain.addFilterRule(new FilterRule<CalculatorHistoryState>() {
@Override
public boolean isFiltered(CalculatorHistoryState object) {
return object == null || StringUtils.isEmpty(object.getEditorState().getText());
}
});
new Filter<CalculatorHistoryState>(filterRulesChain).filter(calculatorHistoryStates.iterator());
return calculatorHistoryStates;
}
@NotNull
protected abstract List<CalculatorHistoryState> getHistoryItems();
@NotNull
public static String getHistoryText(@NotNull CalculatorHistoryState state) {
final StringBuilder result = new StringBuilder();
result.append(state.getEditorState().getText());
result.append(getIdentitySign(state.getDisplayState().getJsclOperation()));
final String expressionResult = state.getDisplayState().getEditorState().getText();
if (expressionResult != null) {
result.append(expressionResult);
}
return result.toString();
}
@NotNull
private static String getIdentitySign(@NotNull JsclOperation jsclOperation) {
return jsclOperation == JsclOperation.simplify ? "" : "=";
}
@Override
public boolean onCreateOptionsMenu(android.view.Menu menu) {
final MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.history_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
boolean result;
switch (item.getItemId()) {
case R.id.history_menu_clear_history:
clearHistory();
result = true;
break;
default:
result = super.onOptionsItemSelected(item);
}
return result;
}
protected abstract void clearHistory();
@NotNull
protected ArrayAdapter<CalculatorHistoryState> getAdapter() {
return adapter;
}
}
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.history;
import android.app.ListActivity;
import android.content.Context;
import android.os.Bundle;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import com.google.ads.AdView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.ads.AdsController;
import org.solovyev.android.calculator.CalculatorModel;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.menu.AMenuBuilder;
import org.solovyev.android.menu.MenuImpl;
import org.solovyev.common.collections.CollectionsUtils;
import org.solovyev.common.equals.Equalizer;
import org.solovyev.common.filter.Filter;
import org.solovyev.common.filter.FilterRule;
import org.solovyev.common.filter.FilterRulesChain;
import org.solovyev.common.text.StringUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* User: serso
* Date: 10/15/11
* Time: 1:13 PM
*/
public abstract class AbstractHistoryActivity extends ListActivity {
public static final Comparator<CalculatorHistoryState> COMPARATOR = new Comparator<CalculatorHistoryState>() {
@Override
public int compare(CalculatorHistoryState state1, CalculatorHistoryState state2) {
if (state1.isSaved() == state2.isSaved()) {
long l = state2.getTime() - state1.getTime();
return l > 0l ? 1 : (l < 0l ? -1 : 0);
} else if (state1.isSaved()) {
return -1;
} else if (state2.isSaved()) {
return 1;
}
return 0;
}
};
@NotNull
private ArrayAdapter<CalculatorHistoryState> adapter;
@Nullable
private AdView adView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.history_activity);
adView = AdsController.getInstance().inflateAd(this);
adapter = new HistoryArrayAdapter(this, getLayoutId(), R.id.history_item, new ArrayList<CalculatorHistoryState>());
setListAdapter(adapter);
final ListView lv = getListView();
lv.setTextFilterEnabled(true);
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(final AdapterView<?> parent,
final View view,
final int position,
final long id) {
useHistoryItem((CalculatorHistoryState) parent.getItemAtPosition(position), AbstractHistoryActivity.this);
}
});
lv.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, final int position, long id) {
final CalculatorHistoryState historyState = (CalculatorHistoryState) parent.getItemAtPosition(position);
final Context context = AbstractHistoryActivity.this;
final HistoryItemMenuData data = new HistoryItemMenuData(historyState, adapter);
final List<HistoryItemMenuItem> menuItems = CollectionsUtils.asList(HistoryItemMenuItem.values());
if (historyState.isSaved()) {
menuItems.remove(HistoryItemMenuItem.save);
} else {
if (isAlreadySaved(historyState)) {
menuItems.remove(HistoryItemMenuItem.save);
}
menuItems.remove(HistoryItemMenuItem.remove);
menuItems.remove(HistoryItemMenuItem.edit);
}
if (historyState.getDisplayState().isValid() && StringUtils.isEmpty(historyState.getDisplayState().getEditorState().getText())) {
menuItems.remove(HistoryItemMenuItem.copy_result);
}
final AMenuBuilder<HistoryItemMenuItem, HistoryItemMenuData> menuBuilder = AMenuBuilder.newInstance(context, MenuImpl.newInstance(menuItems));
menuBuilder.create(data).show();
return true;
}
});
}
@Override
protected void onDestroy() {
if ( this.adView != null ) {
this.adView.destroy();
}
super.onDestroy();
}
protected abstract int getLayoutId();
@Override
protected void onResume() {
super.onResume();
final List<CalculatorHistoryState> historyList = getHistoryList();
try {
this.adapter.setNotifyOnChange(false);
this.adapter.clear();
for (CalculatorHistoryState historyState : historyList) {
this.adapter.add(historyState);
}
} finally {
this.adapter.setNotifyOnChange(true);
}
this.adapter.notifyDataSetChanged();
}
public static boolean isAlreadySaved(@NotNull CalculatorHistoryState historyState) {
assert !historyState.isSaved();
boolean result = false;
try {
historyState.setSaved(true);
if ( CollectionsUtils.contains(historyState, AndroidCalculatorHistoryImpl.instance.getSavedHistory(), new Equalizer<CalculatorHistoryState>() {
@Override
public boolean equals(@Nullable CalculatorHistoryState first, @Nullable CalculatorHistoryState second) {
return first != null && second != null &&
first.getTime() == second.getTime() &&
first.getDisplayState().equals(second.getDisplayState()) &&
first.getEditorState().equals(second.getEditorState());
}
}) ) {
result = true;
}
} finally {
historyState.setSaved(false);
}
return result;
}
public static void useHistoryItem(@NotNull final CalculatorHistoryState historyState, @NotNull AbstractHistoryActivity activity) {
// before evaluating history item - clear display (in order to get Error message in display if evaluation fail)
CalculatorModel.instance.getDisplay().setText("");
CalculatorModel.instance.doTextOperation(new CalculatorModel.TextOperation() {
@Override
public void doOperation(@NotNull EditText editor) {
final EditorHistoryState editorState = historyState.getEditorState();
editor.setText(editorState.getText());
editor.setSelection(editorState.getCursorPosition());
}
}, false, historyState.getDisplayState().getJsclOperation(), true);
CalculatorModel.instance.setCursorOnEnd();
activity.finish();
}
@NotNull
private List<CalculatorHistoryState> getHistoryList() {
final List<CalculatorHistoryState> calculatorHistoryStates = getHistoryItems();
Collections.sort(calculatorHistoryStates, COMPARATOR);
final FilterRulesChain<CalculatorHistoryState> filterRulesChain = new FilterRulesChain<CalculatorHistoryState>();
filterRulesChain.addFilterRule(new FilterRule<CalculatorHistoryState>() {
@Override
public boolean isFiltered(CalculatorHistoryState object) {
return object == null || StringUtils.isEmpty(object.getEditorState().getText());
}
});
new Filter<CalculatorHistoryState>(filterRulesChain).filter(calculatorHistoryStates.iterator());
return calculatorHistoryStates;
}
@NotNull
protected abstract List<CalculatorHistoryState> getHistoryItems();
@NotNull
public static String getHistoryText(@NotNull CalculatorHistoryState state) {
final StringBuilder result = new StringBuilder();
result.append(state.getEditorState().getText());
result.append(getIdentitySign(state.getDisplayState().getJsclOperation()));
final String expressionResult = state.getDisplayState().getEditorState().getText();
if (expressionResult != null) {
result.append(expressionResult);
}
return result.toString();
}
@NotNull
private static String getIdentitySign(@NotNull JsclOperation jsclOperation) {
return jsclOperation == JsclOperation.simplify ? "" : "=";
}
@Override
public boolean onCreateOptionsMenu(android.view.Menu menu) {
final MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.history_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
boolean result;
switch (item.getItemId()) {
case R.id.history_menu_clear_history:
clearHistory();
result = true;
break;
default:
result = super.onOptionsItemSelected(item);
}
return result;
}
protected abstract void clearHistory();
@NotNull
protected ArrayAdapter<CalculatorHistoryState> getAdapter() {
return adapter;
}
}

View File

@@ -0,0 +1,18 @@
package org.solovyev.android.calculator.history;
import android.content.Context;
import android.content.SharedPreferences;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: Solovyev_S
* Date: 20.09.12
* Time: 16:07
*/
public interface AndroidCalculatorHistory extends CalculatorHistory {
void load(@Nullable Context context, @Nullable SharedPreferences preferences);
void save(@NotNull Context context);
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.history;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.R;
import org.solovyev.common.history.HistoryAction;
import java.util.List;
/**
* User: serso
* Date: 10/9/11
* Time: 6:35 PM
*/
public enum AndroidCalculatorHistoryImpl implements AndroidCalculatorHistory {
instance;
@NotNull
private final CalculatorHistoryImpl calculatorHistory = new CalculatorHistoryImpl();
@Override
public void load(@Nullable Context context, @Nullable SharedPreferences preferences) {
if (context != null && preferences != null) {
final String value = preferences.getString(context.getString(R.string.p_calc_history), null);
calculatorHistory.fromXml(value);
}
}
@Override
public void save(@NotNull Context context) {
final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
final SharedPreferences.Editor editor = settings.edit();
editor.putString(context.getString(R.string.p_calc_history), calculatorHistory.toXml());
editor.commit();
}
public void clearSavedHistory(@NotNull Context context) {
calculatorHistory.clearSavedHistory();
save(context);
}
public void removeSavedHistory(@NotNull CalculatorHistoryState historyState, @NotNull Context context) {
historyState.setSaved(false);
calculatorHistory.removeSavedHistory(historyState);
save(context);
}
@Override
public boolean isEmpty() {
return calculatorHistory.isEmpty();
}
@Override
public CalculatorHistoryState getLastHistoryState() {
return calculatorHistory.getLastHistoryState();
}
@Override
public boolean isUndoAvailable() {
return calculatorHistory.isUndoAvailable();
}
@Override
public CalculatorHistoryState undo(@Nullable CalculatorHistoryState currentState) {
return calculatorHistory.undo(currentState);
}
@Override
public boolean isRedoAvailable() {
return calculatorHistory.isRedoAvailable();
}
@Override
public CalculatorHistoryState redo(@Nullable CalculatorHistoryState currentState) {
return calculatorHistory.redo(currentState);
}
@Override
public boolean isActionAvailable(@NotNull HistoryAction historyAction) {
return calculatorHistory.isActionAvailable(historyAction);
}
@Override
public CalculatorHistoryState doAction(@NotNull HistoryAction historyAction, @Nullable CalculatorHistoryState currentState) {
return calculatorHistory.doAction(historyAction, currentState);
}
@Override
public void addState(@Nullable CalculatorHistoryState currentState) {
calculatorHistory.addState(currentState);
}
@NotNull
@Override
public List<CalculatorHistoryState> getStates() {
return calculatorHistory.getStates();
}
@Override
public void clear() {
calculatorHistory.clear();
}
@NotNull
public List<CalculatorHistoryState> getSavedHistory() {
return calculatorHistory.getSavedHistory();
}
@NotNull
public CalculatorHistoryState addSavedState(@NotNull CalculatorHistoryState historyState) {
return calculatorHistory.addSavedState(historyState);
}
@Override
public void fromXml(@NotNull String xml) {
calculatorHistory.fromXml(xml);
}
@Override
public String toXml() {
return calculatorHistory.toXml();
}
@Override
public void clearSavedHistory() {
calculatorHistory.clearSavedHistory();
}
@Override
public void removeSavedHistory(@NotNull CalculatorHistoryState historyState) {
calculatorHistory.removeSavedHistory(historyState);
}
}

View File

@@ -1,149 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.history;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.R;
import org.solovyev.common.history.HistoryAction;
import org.solovyev.common.history.HistoryHelper;
import org.solovyev.common.history.SimpleHistoryHelper;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* User: serso
* Date: 10/9/11
* Time: 6:35 PM
*/
public enum CalculatorHistory implements HistoryHelper<CalculatorHistoryState> {
instance;
// todo serso: not synchronized
private int counter = 0;
@NotNull
private final HistoryHelper<CalculatorHistoryState> history = new SimpleHistoryHelper<CalculatorHistoryState>();
@NotNull
private final List<CalculatorHistoryState> savedHistory = new ArrayList<CalculatorHistoryState> ();
@Override
public boolean isEmpty() {
return this.history.isEmpty();
}
@Override
public CalculatorHistoryState getLastHistoryState() {
return this.history.getLastHistoryState();
}
@Override
public boolean isUndoAvailable() {
return history.isUndoAvailable();
}
@Override
public CalculatorHistoryState undo(@Nullable CalculatorHistoryState currentState) {
return history.undo(currentState);
}
@Override
public boolean isRedoAvailable() {
return history.isRedoAvailable();
}
@Override
public CalculatorHistoryState redo(@Nullable CalculatorHistoryState currentState) {
return history.redo(currentState);
}
@Override
public boolean isActionAvailable(@NotNull HistoryAction historyAction) {
return history.isActionAvailable(historyAction);
}
@Override
public CalculatorHistoryState doAction(@NotNull HistoryAction historyAction, @Nullable CalculatorHistoryState currentState) {
return history.doAction(historyAction, currentState);
}
@Override
public void addState(@Nullable CalculatorHistoryState currentState) {
history.addState(currentState);
}
@NotNull
@Override
public List<CalculatorHistoryState> getStates() {
return history.getStates();
}
@Override
public void clear() {
this.history.clear();
}
public void load(@Nullable Context context, @Nullable SharedPreferences preferences) {
if (context != null && preferences != null) {
final String value = preferences.getString(context.getString(R.string.p_calc_history), null);
this.savedHistory.clear();
HistoryUtils.fromXml(value, this.savedHistory);
for (CalculatorHistoryState historyState : savedHistory) {
historyState.setSaved(true);
historyState.setId(counter++);
}
}
}
public void save(@NotNull Context context) {
final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
final SharedPreferences.Editor editor = settings.edit();
editor.putString(context.getString(R.string.p_calc_history), HistoryUtils.toXml(this.savedHistory));
editor.commit();
}
@NotNull
public List<CalculatorHistoryState> getSavedHistory() {
return Collections.unmodifiableList(savedHistory);
}
@NotNull
public CalculatorHistoryState addSavedState(@NotNull CalculatorHistoryState historyState) {
if (historyState.isSaved()) {
return historyState;
} else {
final CalculatorHistoryState savedState = historyState.clone();
savedState.setId(counter++);
savedState.setSaved(true);
savedHistory.add(savedState);
return savedState;
}
}
public void clearSavedHistory(@NotNull Context context) {
this.savedHistory.clear();
save(context);
}
public void removeSavedHistory(@NotNull CalculatorHistoryState historyState, @NotNull Context context) {
historyState.setSaved(false);
this.savedHistory.remove(historyState);
save(context);
}
}

View File

@@ -1,37 +1,37 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.history;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.R;
import java.util.ArrayList;
import java.util.List;
/**
* User: serso
* Date: 12/18/11
* Time: 7:39 PM
*/
public class HistoryActivityTab extends AbstractHistoryActivity {
@Override
protected int getLayoutId() {
return R.layout.history;
}
@NotNull
@Override
protected List<CalculatorHistoryState> getHistoryItems() {
return new ArrayList<CalculatorHistoryState>(CalculatorHistory.instance.getStates());
}
@Override
protected void clearHistory() {
CalculatorHistory.instance.clear();
getAdapter().clear();
}
}
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.history;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.R;
import java.util.ArrayList;
import java.util.List;
/**
* User: serso
* Date: 12/18/11
* Time: 7:39 PM
*/
public class HistoryActivityTab extends AbstractHistoryActivity {
@Override
protected int getLayoutId() {
return R.layout.history;
}
@NotNull
@Override
protected List<CalculatorHistoryState> getHistoryItems() {
return new ArrayList<CalculatorHistoryState>(AndroidCalculatorHistoryImpl.instance.getStates());
}
@Override
protected void clearHistory() {
AndroidCalculatorHistoryImpl.instance.clear();
getAdapter().clear();
}
}

View File

@@ -1,154 +1,154 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.history;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.text.ClipboardManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.R;
import org.solovyev.android.menu.LabeledMenuItem;
import org.solovyev.common.text.StringUtils;
/**
* User: serso
* Date: 12/18/11
* Time: 3:09 PM
*/
public enum HistoryItemMenuItem implements LabeledMenuItem<HistoryItemMenuData> {
use(R.string.c_use) {
@Override
public void onClick(@NotNull HistoryItemMenuData data, @NotNull Context context) {
if (context instanceof AbstractHistoryActivity) {
AbstractHistoryActivity.useHistoryItem(data.getHistoryState(), (AbstractHistoryActivity) context);
} else {
Log.e(HistoryItemMenuItem.class.getName(), AbstractHistoryActivity.class + " must be passed as context!");
}
}
},
copy_expression(R.string.c_copy_expression) {
@Override
public void onClick(@NotNull HistoryItemMenuData data, @NotNull Context context) {
final CalculatorHistoryState calculatorHistoryState = data.getHistoryState();
final String text = calculatorHistoryState.getEditorState().getText();
if (!StringUtils.isEmpty(text)) {
final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE);
clipboard.setText(text);
Toast.makeText(context, context.getText(R.string.c_expression_copied), Toast.LENGTH_SHORT).show();
}
}
},
copy_result(R.string.c_copy_result) {
@Override
public void onClick(@NotNull HistoryItemMenuData data, @NotNull Context context) {
final CalculatorHistoryState calculatorHistoryState = data.getHistoryState();
final String text = calculatorHistoryState.getDisplayState().getEditorState().getText();
if (!StringUtils.isEmpty(text)) {
final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE);
clipboard.setText(text);
Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show();
}
}
},
save(R.string.c_save) {
@Override
public void onClick(@NotNull final HistoryItemMenuData data, @NotNull final Context context) {
final CalculatorHistoryState historyState = data.getHistoryState();
if (!historyState.isSaved()) {
createEditHistoryDialog(data, context, true);
} else {
Toast.makeText(context, context.getText(R.string.c_history_already_saved), Toast.LENGTH_LONG).show();
}
}
},
edit(R.string.c_edit) {
@Override
public void onClick(@NotNull final HistoryItemMenuData data, @NotNull final Context context) {
final CalculatorHistoryState historyState = data.getHistoryState();
if (historyState.isSaved()) {
createEditHistoryDialog(data, context, false);
} else {
Toast.makeText(context, context.getText(R.string.c_history_must_be_saved), Toast.LENGTH_LONG).show();
}
}
},
remove(R.string.c_remove) {
@Override
public void onClick(@NotNull HistoryItemMenuData data, @NotNull Context context) {
final CalculatorHistoryState historyState = data.getHistoryState();
if (historyState.isSaved()) {
data.getAdapter().remove(historyState);
CalculatorHistory.instance.removeSavedHistory(historyState, context);
Toast.makeText(context, context.getText(R.string.c_history_was_removed), Toast.LENGTH_LONG).show();
data.getAdapter().notifyDataSetChanged();
}
}
};
private static void createEditHistoryDialog(@NotNull final HistoryItemMenuData data, @NotNull final Context context, final boolean save) {
final CalculatorHistoryState historyState = data.getHistoryState();
final LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View editView = layoutInflater.inflate(R.layout.history_edit, null);
final TextView historyExpression = (TextView)editView.findViewById(R.id.history_edit_expression);
historyExpression.setText(AbstractHistoryActivity.getHistoryText(historyState));
final EditText comment = (EditText)editView.findViewById(R.id.history_edit_comment);
comment.setText(historyState.getComment());
final AlertDialog.Builder builder = new AlertDialog.Builder(context)
.setTitle(save ? R.string.c_save_history : R.string.c_edit_history)
.setCancelable(true)
.setNegativeButton(R.string.c_cancel, null)
.setPositiveButton(R.string.c_save, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (save) {
final CalculatorHistoryState savedHistoryItem = CalculatorHistory.instance.addSavedState(historyState);
savedHistoryItem.setComment(comment.getText().toString());
CalculatorHistory.instance.save(context);
// we don't need to add element to the adapter as adapter of another activity must be updated and not this
//data.getAdapter().add(savedHistoryItem);
} else {
historyState.setComment(comment.getText().toString());
CalculatorHistory.instance.save(context);
}
data.getAdapter().notifyDataSetChanged();
Toast.makeText(context, context.getText(R.string.c_history_saved), Toast.LENGTH_LONG).show();
}
})
.setView(editView);
builder.create().show();
}
private final int captionId;
private HistoryItemMenuItem(int captionId) {
this.captionId = captionId;
}
@NotNull
@Override
public String getCaption(@NotNull Context context) {
return context.getString(captionId);
}
}
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.history;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.text.ClipboardManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.R;
import org.solovyev.android.menu.LabeledMenuItem;
import org.solovyev.common.text.StringUtils;
/**
* User: serso
* Date: 12/18/11
* Time: 3:09 PM
*/
public enum HistoryItemMenuItem implements LabeledMenuItem<HistoryItemMenuData> {
use(R.string.c_use) {
@Override
public void onClick(@NotNull HistoryItemMenuData data, @NotNull Context context) {
if (context instanceof AbstractHistoryActivity) {
AbstractHistoryActivity.useHistoryItem(data.getHistoryState(), (AbstractHistoryActivity) context);
} else {
Log.e(HistoryItemMenuItem.class.getName(), AbstractHistoryActivity.class + " must be passed as context!");
}
}
},
copy_expression(R.string.c_copy_expression) {
@Override
public void onClick(@NotNull HistoryItemMenuData data, @NotNull Context context) {
final CalculatorHistoryState calculatorHistoryState = data.getHistoryState();
final String text = calculatorHistoryState.getEditorState().getText();
if (!StringUtils.isEmpty(text)) {
final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE);
clipboard.setText(text);
Toast.makeText(context, context.getText(R.string.c_expression_copied), Toast.LENGTH_SHORT).show();
}
}
},
copy_result(R.string.c_copy_result) {
@Override
public void onClick(@NotNull HistoryItemMenuData data, @NotNull Context context) {
final CalculatorHistoryState calculatorHistoryState = data.getHistoryState();
final String text = calculatorHistoryState.getDisplayState().getEditorState().getText();
if (!StringUtils.isEmpty(text)) {
final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE);
clipboard.setText(text);
Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show();
}
}
},
save(R.string.c_save) {
@Override
public void onClick(@NotNull final HistoryItemMenuData data, @NotNull final Context context) {
final CalculatorHistoryState historyState = data.getHistoryState();
if (!historyState.isSaved()) {
createEditHistoryDialog(data, context, true);
} else {
Toast.makeText(context, context.getText(R.string.c_history_already_saved), Toast.LENGTH_LONG).show();
}
}
},
edit(R.string.c_edit) {
@Override
public void onClick(@NotNull final HistoryItemMenuData data, @NotNull final Context context) {
final CalculatorHistoryState historyState = data.getHistoryState();
if (historyState.isSaved()) {
createEditHistoryDialog(data, context, false);
} else {
Toast.makeText(context, context.getText(R.string.c_history_must_be_saved), Toast.LENGTH_LONG).show();
}
}
},
remove(R.string.c_remove) {
@Override
public void onClick(@NotNull HistoryItemMenuData data, @NotNull Context context) {
final CalculatorHistoryState historyState = data.getHistoryState();
if (historyState.isSaved()) {
data.getAdapter().remove(historyState);
AndroidCalculatorHistoryImpl.instance.removeSavedHistory(historyState, context);
Toast.makeText(context, context.getText(R.string.c_history_was_removed), Toast.LENGTH_LONG).show();
data.getAdapter().notifyDataSetChanged();
}
}
};
private static void createEditHistoryDialog(@NotNull final HistoryItemMenuData data, @NotNull final Context context, final boolean save) {
final CalculatorHistoryState historyState = data.getHistoryState();
final LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View editView = layoutInflater.inflate(R.layout.history_edit, null);
final TextView historyExpression = (TextView)editView.findViewById(R.id.history_edit_expression);
historyExpression.setText(AbstractHistoryActivity.getHistoryText(historyState));
final EditText comment = (EditText)editView.findViewById(R.id.history_edit_comment);
comment.setText(historyState.getComment());
final AlertDialog.Builder builder = new AlertDialog.Builder(context)
.setTitle(save ? R.string.c_save_history : R.string.c_edit_history)
.setCancelable(true)
.setNegativeButton(R.string.c_cancel, null)
.setPositiveButton(R.string.c_save, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (save) {
final CalculatorHistoryState savedHistoryItem = AndroidCalculatorHistoryImpl.instance.addSavedState(historyState);
savedHistoryItem.setComment(comment.getText().toString());
AndroidCalculatorHistoryImpl.instance.save(context);
// we don't need to add element to the adapter as adapter of another activity must be updated and not this
//data.getAdapter().add(savedHistoryItem);
} else {
historyState.setComment(comment.getText().toString());
AndroidCalculatorHistoryImpl.instance.save(context);
}
data.getAdapter().notifyDataSetChanged();
Toast.makeText(context, context.getText(R.string.c_history_saved), Toast.LENGTH_LONG).show();
}
})
.setView(editView);
builder.create().show();
}
private final int captionId;
private HistoryItemMenuItem(int captionId) {
this.captionId = captionId;
}
@NotNull
@Override
public String getCaption(@NotNull Context context) {
return context.getString(captionId);
}
}

View File

@@ -1,37 +1,37 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.history;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.R;
import java.util.ArrayList;
import java.util.List;
/**
* User: serso
* Date: 12/18/11
* Time: 7:40 PM
*/
public class SavedHistoryActivityTab extends AbstractHistoryActivity {
@Override
protected int getLayoutId() {
return R.layout.saved_history;
}
@NotNull
@Override
protected List<CalculatorHistoryState> getHistoryItems() {
return new ArrayList<CalculatorHistoryState>(CalculatorHistory.instance.getSavedHistory());
}
@Override
protected void clearHistory() {
CalculatorHistory.instance.clearSavedHistory(this);
getAdapter().clear();
}
}
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.history;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.R;
import java.util.ArrayList;
import java.util.List;
/**
* User: serso
* Date: 12/18/11
* Time: 7:40 PM
*/
public class SavedHistoryActivityTab extends AbstractHistoryActivity {
@Override
protected int getLayoutId() {
return R.layout.saved_history;
}
@NotNull
@Override
protected List<CalculatorHistoryState> getHistoryItems() {
return new ArrayList<CalculatorHistoryState>(AndroidCalculatorHistoryImpl.instance.getSavedHistory());
}
@Override
protected void clearHistory() {
AndroidCalculatorHistoryImpl.instance.clearSavedHistory(this);
getAdapter().clear();
}
}

View File

@@ -1,85 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.model;
import jscl.MathEngine;
import jscl.NumeralBase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.common.text.StringUtils;
/**
* User: serso
* Date: 12/15/11
* Time: 9:01 PM
*/
public abstract class AbstractNumberBuilder {
@NotNull
protected final MathEngine engine;
@Nullable
protected StringBuilder numberBuilder = null;
@Nullable
protected NumeralBase nb;
protected AbstractNumberBuilder(@NotNull MathEngine engine) {
this.engine = engine;
this.nb = engine.getNumeralBase();
}
/**
* Method determines if we can continue to process current number
*
* @param mathTypeResult current math type result
* @return true if we can continue of processing of current number, if false - new number should be constructed
*/
protected boolean canContinue(@NotNull MathType.Result mathTypeResult) {
boolean result = mathTypeResult.getMathType().getGroupType() == MathType.MathGroupType.number &&
!spaceBefore(mathTypeResult) &&
numeralBaseCheck(mathTypeResult) &&
numeralBaseInTheStart(mathTypeResult.getMathType()) || isSignAfterE(mathTypeResult);
return result;
}
private boolean spaceBefore(@NotNull MathType.Result mathTypeResult) {
return numberBuilder == null && StringUtils.isEmpty(mathTypeResult.getMatch().trim());
}
private boolean numeralBaseInTheStart(@NotNull MathType mathType) {
return mathType != MathType.numeral_base || numberBuilder == null;
}
private boolean numeralBaseCheck(@NotNull MathType.Result mathType) {
return mathType.getMathType() != MathType.digit || getNumeralBase().getAcceptableCharacters().contains(mathType.getMatch().charAt(0));
}
private boolean isSignAfterE(@NotNull MathType.Result mathTypeResult) {
if (!isHexMode()) {
if ("-".equals(mathTypeResult.getMatch()) || "+".equals(mathTypeResult.getMatch())) {
final StringBuilder localNb = numberBuilder;
if (localNb != null && localNb.length() > 0) {
if (localNb.charAt(localNb.length() - 1) == MathType.POWER_10) {
return true;
}
}
}
}
return false;
}
public boolean isHexMode() {
return nb == NumeralBase.hex || (nb == null && engine.getNumeralBase() == NumeralBase.hex);
}
@NotNull
protected NumeralBase getNumeralBase() {
return nb == null ? engine.getNumeralBase() : nb;
}
}

View File

@@ -15,10 +15,7 @@ import jscl.math.operator.Operator;
import jscl.text.ParseInterruptedException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.CalculatorApplication;
import org.solovyev.android.calculator.CalculatorParseException;
import org.solovyev.android.calculator.JCalculatorEngine;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.text.TextProcessor;
import org.solovyev.android.msg.AndroidMessage;

View File

@@ -1,73 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.model;
import jscl.AbstractJsclArithmeticException;
import org.jetbrains.annotations.NotNull;
import org.solovyev.common.exceptions.SersoException;
import org.solovyev.common.msg.Message;
import org.solovyev.common.msg.MessageType;
import java.util.List;
import java.util.Locale;
/**
* User: serso
* Date: 12/8/11
* Time: 1:27 AM
*/
public class CalculatorEvalException extends SersoException implements Message {
@NotNull
private final Message message;
@NotNull
private final String expression;
public CalculatorEvalException(@NotNull Message message, @NotNull Throwable cause, String expression) {
super(cause);
this.message = message;
this.expression = expression;
}
@NotNull
public String getExpression() {
return expression;
}
@NotNull
@Override
public String getMessageCode() {
return this.message.getMessageCode();
}
@NotNull
@Override
public List<Object> getParameters() {
return this.message.getParameters();
}
@NotNull
@Override
public MessageType getMessageType() {
return this.message.getMessageType();
}
@Override
@NotNull
public String getLocalizedMessage() {
return this.message.getLocalizedMessage(Locale.getDefault());
}
@NotNull
@Override
public String getLocalizedMessage(@NotNull Locale locale) {
return this.message.getLocalizedMessage(locale);
}
}

View File

@@ -1,55 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.model;
import jscl.MathEngine;
import jscl.NumeralBase;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.math.MathType;
/**
* User: serso
* Date: 12/15/11
* Time: 8:33 PM
*/
public class LiteNumberBuilder extends AbstractNumberBuilder {
public LiteNumberBuilder(@NotNull MathEngine engine) {
super(engine);
this.nb = engine.getNumeralBase();
}
public void process(@NotNull MathType.Result mathTypeResult) {
if (canContinue(mathTypeResult)) {
// let's continue building number
if (numberBuilder == null) {
// if new number => create new builder
numberBuilder = new StringBuilder();
}
if (mathTypeResult.getMathType() != MathType.numeral_base) {
// just add matching string
numberBuilder.append(mathTypeResult.getMatch());
} else {
// set explicitly numeral base (do not include it into number)
nb = NumeralBase.getByPrefix(mathTypeResult.getMatch());
}
} else {
// process current number (and go to the next one)
if (numberBuilder != null) {
numberBuilder = null;
// must set default numeral base (exit numeral base mode)
nb = engine.getNumeralBase();
}
}
}
}

View File

@@ -1,202 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.model;
import jscl.MathContext;
import jscl.MathEngine;
import jscl.NumeralBase;
import jscl.math.numeric.Real;
import jscl.text.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.common.MutableObject;
import java.util.ArrayList;
import java.util.List;
/**
* User: serso
* Date: 10/23/11
* Time: 2:57 PM
*/
public class NumberBuilder extends AbstractNumberBuilder {
public NumberBuilder(@NotNull MathEngine engine) {
super(engine);
}
/**
* 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)
*/
@NotNull
public MathType.Result process(@NotNull StringBuilder text, @NotNull MathType.Result mathTypeResult, @Nullable MutableObject<Integer> offset) {
final MathType.Result possibleResult;
if (canContinue(mathTypeResult)) {
// let's continue building number
if (numberBuilder == null) {
// if new number => create new builder
numberBuilder = new StringBuilder();
}
if (mathTypeResult.getMathType() != MathType.numeral_base) {
// just add matching string
numberBuilder.append(mathTypeResult.getMatch());
} else {
// set explicitly numeral base (do not include it into number)
nb = NumeralBase.getByPrefix(mathTypeResult.getMatch());
}
possibleResult = null;
} else {
// process current number (and go to the next one)
possibleResult = processNumber(text, offset);
}
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)
*/
@Nullable
public MathType.Result processNumber(@NotNull StringBuilder text, @Nullable MutableObject<Integer> offset) {
// total number of trimmed chars
int trimmedChars = 0;
String number = null;
// save numeral base (as later it might be replaced)
final NumeralBase localNb = getNumeralBase();
if (numberBuilder != null) {
try {
number = numberBuilder.toString();
// let's get rid of unnecessary characters (grouping separators, + after E)
final List<String> tokens = new ArrayList<String>();
tokens.addAll(MathType.grouping_separator.getTokens());
// + after E can be omitted: 10+E = 10E (NOTE: - cannot be omitted )
tokens.add("+");
for (String groupingSeparator : tokens) {
final String trimmedNumber = number.replace(groupingSeparator, "");
trimmedChars += number.length() - trimmedNumber.length();
number = trimmedNumber;
}
// check if number still valid
toDouble(number, getNumeralBase(), engine);
} catch (NumberFormatException e) {
// number is not valid => stop
number = null;
}
numberBuilder = null;
// must set default numeral base (exit numeral base mode)
nb = engine.getNumeralBase();
}
return replaceNumberInText(text, number, trimmedChars, offset, localNb, engine);
}
@Nullable
private static MathType.Result replaceNumberInText(@NotNull StringBuilder text,
@Nullable String number,
int trimmedChars,
@Nullable MutableObject<Integer> offset,
@NotNull NumeralBase nb,
@NotNull final MathEngine engine) {
MathType.Result result = null;
if (number != null) {
// in any case remove old number from text
final int oldNumberLength = number.length() + trimmedChars;
text.delete(text.length() - oldNumberLength, text.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;
}
@NotNull
private static String formatNumber(@NotNull String number, @NotNull NumeralBase nb, @NotNull MathEngine engine) {
String result;
int indexOfDot = number.indexOf('.');
if (indexOfDot < 0) {
int indexOfE;
if (nb == NumeralBase.hex) {
indexOfE = -1;
} else {
indexOfE = number.indexOf(MathType.POWER_10);
}
if (indexOfE < 0) {
result = engine.addGroupingSeparators(nb, number);
} else {
final String partBeforeE;
if (indexOfE != 0) {
partBeforeE = engine.addGroupingSeparators(nb, number.substring(0, indexOfE));
} else {
partBeforeE = "";
}
result = partBeforeE + number.substring(indexOfE);
}
} else {
final String integerPart;
if (indexOfDot != 0) {
integerPart = engine.addGroupingSeparators(nb, number.substring(0, indexOfDot));
} else {
integerPart = "";
}
result = integerPart + number.substring(indexOfDot);
}
return result;
}
@NotNull
private static Double toDouble(@NotNull String s, @NotNull NumeralBase nb, @NotNull final MathContext mc) throws NumberFormatException {
final NumeralBase defaultNb = mc.getNumeralBase();
try {
mc.setNumeralBase(nb);
try {
return JsclIntegerParser.parser.parse(Parser.Parameters.newInstance(s, new MutableInt(0), mc), null).content().doubleValue();
} catch (ParseException e) {
try {
return ((Real) DoubleParser.parser.parse(Parser.Parameters.newInstance(s, new MutableInt(0), mc), null).content()).doubleValue();
} catch (ParseException e1) {
throw new NumberFormatException();
}
}
} finally {
mc.setNumeralBase(defaultNb);
}
}
}

View File

@@ -1,65 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.model;
import jscl.math.function.IConstant;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* User: serso
* Date: 10/18/11
* Time: 10:07 PM
*/
public class PreparedExpression implements CharSequence{
@NotNull
private String expression;
@NotNull
private List<IConstant> undefinedVars;
public PreparedExpression(@NotNull String expression, @NotNull List<IConstant> undefinedVars) {
this.expression = expression;
this.undefinedVars = undefinedVars;
}
@NotNull
public String getExpression() {
return expression;
}
public boolean isExistsUndefinedVar() {
return !this.undefinedVars.isEmpty();
}
@NotNull
public List<IConstant> getUndefinedVars() {
return undefinedVars;
}
@Override
public int length() {
return expression.length();
}
@Override
public char charAt(int i) {
return expression.charAt(i);
}
@Override
public CharSequence subSequence(int i, int i1) {
return expression.subSequence(i, i1);
}
@Override
public String toString() {
return this.expression;
}
}

View File

@@ -1,153 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.model;
import jscl.math.function.IConstant;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.CalculatorApplication;
import org.solovyev.android.calculator.CalculatorParseException;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.text.TextProcessor;
import org.solovyev.android.msg.AndroidMessage;
import org.solovyev.common.StartsWithFinder;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.common.collections.CollectionsUtils;
import org.solovyev.common.msg.MessageType;
import java.util.ArrayList;
import java.util.List;
public class ToJsclTextProcessor implements TextProcessor<PreparedExpression, String> {
@NotNull
private static final Integer MAX_DEPTH = 20;
@NotNull
private static final TextProcessor<PreparedExpression, String> instance = new ToJsclTextProcessor();
private ToJsclTextProcessor() {
}
@NotNull
public static TextProcessor<PreparedExpression, String> getInstance() {
return instance;
}
@Override
@NotNull
public PreparedExpression process(@NotNull String s) throws CalculatorParseException {
return processWithDepth(s, 0, new ArrayList<IConstant>());
}
private static PreparedExpression processWithDepth(@NotNull String s, int depth, @NotNull List<IConstant> undefinedVars) throws CalculatorParseException {
return replaceVariables(processExpression(s).toString(), depth, undefinedVars);
}
@NotNull
private static StringBuilder processExpression(@NotNull String s) throws CalculatorParseException {
final StartsWithFinder startsWithFinder = new StartsWithFinder(s, 0);
final StringBuilder result = new StringBuilder();
MathType.Result mathTypeResult = null;
MathType.Result mathTypeBefore;
final LiteNumberBuilder nb = new LiteNumberBuilder(CalculatorEngine.instance.getEngine());
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == ' ') continue;
startsWithFinder.setI(i);
mathTypeBefore = mathTypeResult == null ? null : mathTypeResult;
mathTypeResult = MathType.getType(s, i, nb.isHexMode());
nb.process(mathTypeResult);
if (mathTypeBefore != null) {
final MathType current = mathTypeResult.getMathType();
if (current.isNeedMultiplicationSignBefore(mathTypeBefore.getMathType())) {
result.append("*");
}
}
if (mathTypeBefore != null &&
(mathTypeBefore.getMathType() == MathType.function || mathTypeBefore.getMathType() == MathType.operator) &&
CollectionsUtils.find(MathType.openGroupSymbols, startsWithFinder) != null) {
final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_5, MessageType.error, CalculatorApplication.getInstance(), mathTypeBefore.getMatch());
throw new CalculatorParseException(i, s, androidMessage);
}
i = mathTypeResult.processToJscl(result, i);
}
return result;
}
@NotNull
private static PreparedExpression replaceVariables(@NotNull final String s, int depth, @NotNull List<IConstant> undefinedVars) throws CalculatorParseException {
if (depth >= MAX_DEPTH) {
final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_6, MessageType.error, CalculatorApplication.getInstance());
throw new CalculatorParseException(s, androidMessage);
} else {
depth++;
}
final StartsWithFinder startsWithFinder = new StartsWithFinder(s, 0);
final StringBuilder result = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
startsWithFinder.setI(i);
int offset = 0;
String functionName = CollectionsUtils.find(MathType.function.getTokens(), startsWithFinder);
if (functionName == null) {
String operatorName = CollectionsUtils.find(MathType.operator.getTokens(), startsWithFinder);
if (operatorName == null) {
String varName = CollectionsUtils.find(CalculatorEngine.instance.getVarsRegistry().getNames(), startsWithFinder);
if (varName != null) {
final IConstant var = CalculatorEngine.instance.getVarsRegistry().get(varName);
if (var != null) {
if (!var.isDefined()) {
undefinedVars.add(var);
result.append(varName);
offset = varName.length();
} else {
final String value = var.getValue();
assert value != null;
if ( var.getDoubleValue() != null ) {
//result.append(value);
// NOTE: append varName as JSCL engine will convert it to double if needed
result.append(varName);
} else {
result.append("(").append(processWithDepth(value, depth, undefinedVars)).append(")");
}
offset = varName.length();
}
}
}
} else {
result.append(operatorName);
offset = operatorName.length();
}
} else {
result.append(functionName);
offset = functionName.length();
}
if (offset == 0) {
result.append(s.charAt(i));
} else {
i += offset - 1;
}
}
return new PreparedExpression(result.toString(), undefinedVars);
}
}

View File

@@ -36,8 +36,8 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.CalculatorParseException;
import org.solovyev.android.calculator.model.PreparedExpression;
import org.solovyev.android.calculator.model.ToJsclTextProcessor;
import org.solovyev.android.calculator.PreparedExpression;
import org.solovyev.android.calculator.ToJsclTextProcessor;
import org.solovyev.common.MutableObject;
import java.io.Serializable;

View File

@@ -13,7 +13,7 @@ import org.solovyev.android.calculator.CalculatorModel;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.calculator.CalculatorParseException;
import org.solovyev.android.calculator.model.ToJsclTextProcessor;
import org.solovyev.android.calculator.ToJsclTextProcessor;
import org.solovyev.common.MutableObject;
import org.solovyev.common.text.StringUtils;

View File

@@ -9,7 +9,10 @@ package org.solovyev.android.calculator.view;
import jscl.MathContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.AbstractNumberBuilder;
import org.solovyev.android.calculator.CalculatorParseException;
import org.solovyev.android.calculator.LiteNumberBuilder;
import org.solovyev.android.calculator.NumberBuilder;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.android.calculator.model.*;
import org.solovyev.android.calculator.text.TextProcessor;