0

I have some code here for a calculator that I'm pretty sure should be working (I remember it working in the past), but it's acting up.

I've checked all the arrayList.get() to make sure they're pointing to the right index, and there are the correct number of arrayList.remove()'s per loop.

The only other thing I can think of is that potentially the if loop that checks if the result is a decimal or not (right down near the bottom of the method) is messing with things.

Here is the code:

public void onClickEquals (View view) {

    TextView textViewError = (TextView) findViewById(R.id.textViewCalcPrevRes);
    TextView textViewCalcPrevRes = (TextView) findViewById(R.id.textViewCalcPrevRes);
    TextView textViewCalcCurrExp = (TextView) findViewById(R.id.textViewCalcCurrExp);
    TextView textViewCalcPrevExp = (TextView) findViewById(R.id.textViewCalcPrevExp);
    double calc = 0;
    String calcOutputStr = "";
    String tempString = "";
    int c = arrayList.size();

    try {
        textViewError.setText("");
        //i.e. array [2,+,3,*,4,-,3] size(c) = 7, so [2,+,3,*,4,-,3]
        while (c != 1) {
            if (c > 3) {
                if (arrayList.get(3).contains("×") || arrayList.get(3).contains("÷")) {
                    if (arrayList.get(3).contains("×")) {calc = Double.parseDouble(arrayList.get(2)) * Double.parseDouble(arrayList.get(4));}
                    if (arrayList.get(3).contains("÷")) {calc = Double.parseDouble(arrayList.get(2)) / Double.parseDouble(arrayList.get(4));}

                    //calc = 12 ;array = [2,+,3,*,4,-,3]
                    arrayList.remove(2); //[2,+,*,4,-,3]
                    arrayList.remove(2); //[2,+,4,-,3]
                    arrayList.remove(2); //[2,+,-,3]
                    arrayList.add(2, Double.toString(calc)); //[2,+,12,-,3]
                    c = arrayList.size(); // size(c) = 5
                } else {
                    //[2,+,12,-,3]
                    if (arrayList.get(1).contains("+")) {calc = Double.parseDouble(arrayList.get(0)) + Double.parseDouble(arrayList.get(2));}
                    if (arrayList.get(1).contains("-")) {calc = Double.parseDouble(arrayList.get(0)) - Double.parseDouble(arrayList.get(2));}
                    if (arrayList.get(1).contains("×")) {calc = Double.parseDouble(arrayList.get(0)) * Double.parseDouble(arrayList.get(2));}
                    if (arrayList.get(1).contains("÷")) {calc = Double.parseDouble(arrayList.get(0)) / Double.parseDouble(arrayList.get(2));}
                    //calc = 14
                    arrayList.remove(0); //[+,12,-,3]
                    arrayList.remove(0); //[12,-,3]
                    arrayList.remove(0); //[-,3]
                    arrayList.add(0, Double.toString(calc)); //[14,-,3]
                    c = arrayList.size(); // size(c) = 3
                }
            }
            // size(c) <= 3
            else {
                if (arrayList.get(1).contains("+")) {calc = Double.parseDouble(arrayList.get(0)) + Double.parseDouble(arrayList.get(2));}
                if (arrayList.get(1).contains("-")) {calc = Double.parseDouble(arrayList.get(0)) - Double.parseDouble(arrayList.get(2));}
                if (arrayList.get(1).contains("×")) {calc = Double.parseDouble(arrayList.get(0)) * Double.parseDouble(arrayList.get(2));}
                if (arrayList.get(1).contains("÷")) {calc = Double.parseDouble(arrayList.get(0)) / Double.parseDouble(arrayList.get(2));}
                //calc = 11
                arrayList.remove(0); //[-,3]
                arrayList.remove(0); //[3]
                arrayList.remove(0); //[null]
                arrayList.add(0, Double.toString(calc)); // [9]
                c = arrayList.size(); // size(c) = 1
                prevCalc = Double.toString(calc);
            }
            //CHECK IF DECIMAL - SHOULD BE OUTSIDE WHILE LOOP
            //check if calc is a whole number; if yes, convert to string and enter into tempString, remove decimal and enter into calcOutputStr ready for display on screen.
            if (calc % 1 == 0) {
                tempString = Double.toString(calc);
                if (tempString != null) {
                    tempString = tempString.substring(0, tempString.length() - 2);
                }
                calcOutputStr = tempString;
                arrayList.clear();
            }
            //if calc is a decimal convert to string ready for display on screen.
            else {
                calcOutputStr = Double.toString(calc);
                arrayList.clear();
            }
        }
            textViewCalcPrevExp.setText(textViewCalcCurrExp.getText()); //copy text from textViewCalcCurrExp to textViewCalcPrevExp
            textViewCalcCurrExp.setText(""); //remove text from textViewCalcCurrExp
            textViewCalcPrevRes.setText(calcOutputStr); //display calc
            stringInput = "";
            stringInputWithOp="";
    }
    catch (Exception e) {
        e.printStackTrace();
        textViewCalcPrevExp.setText(textViewCalcCurrExp.getText());
        textViewCalcCurrExp.setText("");
        stringInput="";
        stringInputWithOp="";
        arrayList.clear();
        textViewError.setText("ERROR");
    }
}

I'll give two scenarios to help illustrate my issue:

  1. arrayList = [2,+,2]; upon running this method, textViewCalcPrevRes displays 4

  2. arrayList = [2,+,2,+,2]; upon running this method, textViewCalcPrevRes displays ERROR, due to the method throwing an exception and printing the stack trace in the console (see next code block). The really weird thing is that the catch{} statement should output ERROR to textViewError and not textViewCalcPrevRes.

Here's the stack trace outputted to the console in scenario 2:

W/System.err: java.lang.IndexOutOfBoundsException: Index: 1, Size: 0
W/System.err:     at java.util.ArrayList.get(ArrayList.java:411)
W/System.err:     at com.st1.u3141294.sparkscientificcalculator.sparkMain$override.onClickEquals(sparkMain.java:126)
W/System.err:     at com.st1.u3141294.sparkscientificcalculator.sparkMain$override.access$dispatch(sparkMain.java)
W/System.err:     at com.st1.u3141294.sparkscientificcalculator.sparkMain.onClickEquals(sparkMain.java:0)
W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
W/System.err:     at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288)
W/System.err:     at android.view.View.performClick(View.java:5610)
W/System.err:     at android.view.View$PerformClick.run(View.java:22260)
W/System.err:     at android.os.Handler.handleCallback(Handler.java:751)
W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:95)
W/System.err:     at android.os.Looper.loop(Looper.java:154)
W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:6077)
W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)

Obviously it's throwing an IndexOutOfBounds Exception, however I'm really not sure how/why.

Any ideas please?

EDIT: Further "investigation" suggests that the origin of the exception is from this line of code (marked with //-->):

// size(c) <= 3
            else {
           //-->if (arrayList.get(1).contains("+")) {calc = Double.parseDouble(arrayList.get(0)) + Double.parseDouble(arrayList.get(2));}
                if (arrayList.get(1).contains("-")) {calc = Double.parseDouble(arrayList.get(0)) - Double.parseDouble(arrayList.get(2));}
                if (arrayList.get(1).contains("×")) {calc = Double.parseDouble(arrayList.get(0)) * Double.parseDouble(arrayList.get(2));}
                if (arrayList.get(1).contains("÷")) {calc = Double.parseDouble(arrayList.get(0)) / Double.parseDouble(arrayList.get(2));}

EDIT: @Code-Apprentice:

If we use scenario 2 as the contents of the arrayList (that is [2,+,2,+,2]), then my code should work. Following the trace:

First block of code run because c = arrayList.size() = 5, and there are no multiplication/division operators in the arrayList, so it would go to the else loop:

while (c != 1) {    
    if (c > 3) {
    //here is code only run if there are multiplication or division operators in arrayList
    } else { 
        if (arrayList.get(1).contains("+")) {calc = Double.parseDouble(arrayList.get(0)) + Double.parseDouble(arrayList.get(2));}
    }
        arrayList.remove(0); //bringing arrayList down to [+,2,+,2]
        arrayList.remove(0); //then [2,+,2]
        arrayList.remove(0); //then [+,2]
        arrayList.add(0, Double.toString(calc)); //then adding 4 (calc) to arrayList[0] would give [4,+,2]
        c = arrayList.size(); // therefore arrayList.size() = 3
}

Now, since c = arrayList.size() = 3:

// size(c) <= 3
        else {
            //4+2
            if (arrayList.get(1).contains("+")) {calc = Double.parseDouble(arrayList.get(0)) + Double.parseDouble(arrayList.get(2));}
        }
        //new value of calc = 6
        arrayList.remove(0); //gives [+,2]
        arrayList.remove(0); //gives [2]
        arrayList.remove(0); //arrayList is now empty: [null]
        arrayList.add(0, Double.toString(calc)); // arrayList populated with calc --> [6]
        c = arrayList.size(); // size(c) = 1
        prevCalc = Double.toString(calc); //puts this calculation into memory for next calculation if needed
rst-2cv
  • 1,130
  • 1
  • 15
  • 31
  • Commenting to bump. – rst-2cv Oct 22 '16 at 09:56
  • "Index: 1, Size: 0" This means the list is empty. You need to use the debugged to figure out why this is so when you expect otherwise. – Code-Apprentice Oct 22 '16 at 10:04
  • If you need further help, please tell us which line causes the crash. – Code-Apprentice Oct 22 '16 at 10:05
  • Unsolicited advice: findViewById() is expensive. You should do these calls once in onCreate() and save the references as field variables. – Code-Apprentice Oct 22 '16 at 10:08
  • @Code-Apprentice I edited the OP to show the code would be run. By my eyes it looks like nothing should be wrong. Look at the bottom of the post. – rst-2cv Oct 22 '16 at 10:22
  • According to your comments, you call remove() three times when the list only has two elements. Clearly this is incorrect. – Code-Apprentice Oct 22 '16 at 10:29
  • @Code-Apprentice where do you see that it has 2 elements? First or second block of code in my example? – rst-2cv Oct 22 '16 at 10:32
  • Sorry I misunderstood the annotations in the comments. Have you run the debugger to see where the elements in the list differ from what you expect in this example? – Code-Apprentice Oct 22 '16 at 10:40
  • All good, I just posted a solution thanks to Sarasranglt's suggestion. Now, @Code-Apprentice can I please ask you to elaborate on calling findViewById() once in onCreate()? – rst-2cv Oct 22 '16 at 10:44
  • 1. Declare all view variable as fields instead of as local variables. 2. Call findViewById() for each one in onCreate() rather than your click listener. This will increase the response time for your calculations. – Code-Apprentice Oct 22 '16 at 11:39

2 Answers2

2

Simply, log ur arrayList size again and again in the code . You'll get to know when its empty.

Log.d("Buggy List Size ",  arrayList.size()+"");
Sarasranglt
  • 3,819
  • 4
  • 23
  • 36
0

Turns out I made a rookie mistake, the CHECK IF DECIMAL loop was inside the while loop, meaning that as soon as the first calculation had been made, it would attempt to output the result. Instead of calculating 2+2+2, it only calculated 2+2 and then would clear the arrayList, causing the IndexOutOfBounds exception.

Here is the fixed code (with the CHECK IF DECIMAL loop outside the while loop, like it should be)

public void onClickEquals (View view) {

    TextView textViewError = (TextView) findViewById(R.id.textViewCalcPrevRes);
    TextView textViewCalcPrevRes = (TextView) findViewById(R.id.textViewCalcPrevRes);
    TextView textViewCalcCurrExp = (TextView) findViewById(R.id.textViewCalcCurrExp);
    TextView textViewCalcPrevExp = (TextView) findViewById(R.id.textViewCalcPrevExp);
    double calc = 0;
    String calcOutputStr = "";
    String tempString = "";
    int c = arrayList.size();

    try {
        textViewError.setText("");
        //i.e. array [2,+,3,*,4,-,3] size(c) = 7, so [2,+,3,*,4,-,3]
        while (c != 1) {
            if (c > 3) {
                if (arrayList.get(3).contains("×") || arrayList.get(3).contains("÷")) {
                    if (arrayList.get(3).contains("×")) {calc = Double.parseDouble(arrayList.get(2)) * Double.parseDouble(arrayList.get(4));}
                    if (arrayList.get(3).contains("÷")) {calc = Double.parseDouble(arrayList.get(2)) / Double.parseDouble(arrayList.get(4));}

                    //calc = 12 ;array = [2,+,3,*,4,-,3]
                    Log.d("BuggyListSizeB4Remove1", arrayList.size()+"");
                    arrayList.remove(2); //[2,+,*,4,-,3]
                    Log.d("BuggyListSizeB4Remove2", arrayList.size()+"");
                    arrayList.remove(2); //[2,+,4,-,3]
                    Log.d("BuggyListSizeB4Remove3", arrayList.size()+"");
                    arrayList.remove(2); //[2,+,-,3]
                    Log.d("BuggyListSizeAfter3", arrayList.size()+"");
                    arrayList.add(2, Double.toString(calc)); //[2,+,12,-,3]
                    c = arrayList.size(); // size(c) = 5
                    Log.d("BuggyListSize AfterCalc", arrayList.size()+"");
                } else {
                    //[2,+,12,-,3]
                    if (arrayList.get(1).contains("+")) {calc = Double.parseDouble(arrayList.get(0)) + Double.parseDouble(arrayList.get(2));}
                    if (arrayList.get(1).contains("-")) {calc = Double.parseDouble(arrayList.get(0)) - Double.parseDouble(arrayList.get(2));}
                    if (arrayList.get(1).contains("×")) {calc = Double.parseDouble(arrayList.get(0)) * Double.parseDouble(arrayList.get(2));}
                    if (arrayList.get(1).contains("÷")) {calc = Double.parseDouble(arrayList.get(0)) / Double.parseDouble(arrayList.get(2));}
                    //calc = 14
                    Log.d("BuggyListSizeB4Remove1", arrayList.size()+"");
                    arrayList.remove(0); //[+,12,-,3]
                    Log.d("BuggyListSizeB4Remove2", arrayList.size()+"");
                    arrayList.remove(0); //[12,-,3]
                    Log.d("BuggyListSizeB4Remove3", arrayList.size()+"");
                    arrayList.remove(0); //[-,3]
                    Log.d("BuggyListSizeAfter3", arrayList.size()+"");
                    arrayList.add(0, Double.toString(calc)); //[14,-,3]
                    c = arrayList.size(); // size(c) = 3
                    Log.d("BuggyListSize AfterCalc", arrayList.size()+"");
                }
            }
            // size(c) <= 3
            else {
                if (arrayList.get(1).contains("+")) {calc = Double.parseDouble(arrayList.get(0)) + Double.parseDouble(arrayList.get(2));}
                if (arrayList.get(1).contains("-")) {calc = Double.parseDouble(arrayList.get(0)) - Double.parseDouble(arrayList.get(2));}
                if (arrayList.get(1).contains("×")) {calc = Double.parseDouble(arrayList.get(0)) * Double.parseDouble(arrayList.get(2));}
                if (arrayList.get(1).contains("÷")) {calc = Double.parseDouble(arrayList.get(0)) / Double.parseDouble(arrayList.get(2));}
                //calc = 11
                Log.d("BuggyListSizeB4Remove1", arrayList.size()+"");
                arrayList.remove(0); //[-,3]
                Log.d("BuggyListSizeB4Remove2", arrayList.size()+"");
                arrayList.remove(0); //[3]
                Log.d("BuggyListSizeB4Remove3", arrayList.size()+"");
                arrayList.remove(0); //[null]
                Log.d("BuggyListSizeAfter3", arrayList.size()+"");
                arrayList.add(0, Double.toString(calc)); // [9]
                c = arrayList.size(); // size(c) = 1
                prevCalc = Double.toString(calc);
                Log.d("BuggyListSize AfterCalc", arrayList.size()+"");
            }
        }
        //CHECK IF DECIMAL
        //check if calc is a whole number; if yes, convert to string and enter into tempString, remove decimal and enter into calcOutputStr ready for display on screen.
        if (calc % 1 == 0) {
            tempString = Double.toString(calc);
            if (tempString != null) {
                tempString = tempString.substring(0, tempString.length() - 2);
            }
            calcOutputStr = tempString;
            arrayList.clear();
        }
        //if calc is a decimal convert to string ready for display on screen.
        else {
            calcOutputStr = Double.toString(calc);
            arrayList.clear();
        }
        //output to textViews
            textViewCalcPrevExp.setText(textViewCalcCurrExp.getText()); //copy text from textViewCalcCurrExp to textViewCalcPrevExp
            textViewCalcCurrExp.setText(""); //remove text from textViewCalcCurrExp
            textViewCalcPrevRes.setText(calcOutputStr); //display calc
            stringInput = "";
            stringInputWithOp="";
    }
    catch (Exception e) {
        e.printStackTrace();
        textViewCalcPrevExp.setText(textViewCalcCurrExp.getText());
        textViewCalcCurrExp.setText("");
        stringInput="";
        stringInputWithOp="";
        arrayList.clear();
        textViewError.setText("ERROR");
    }
}
rst-2cv
  • 1,130
  • 1
  • 15
  • 31