14b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen/*
24b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen * Copyright (C) 2014 The Android Open Source Project
34b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen *
44b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen * Licensed under the Apache License, Version 2.0 (the "License");
54b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen * you may not use this file except in compliance with the License.
64b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen * You may obtain a copy of the License at
74b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen *
84b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen *   http://www.apache.org/licenses/LICENSE-2.0
94b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen *
104b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen * Unless required by applicable law or agreed to in writing, software
114b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen * distributed under the License is distributed on an "AS IS" BASIS,
124b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen * See the License for the specific language governing permissions and
144b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen * limitations under the License.
154b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen */
164b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
174b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenpackage com.android.calculator2;
184b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
194b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.content.Context;
204b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.text.SpannableStringBuilder;
214b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.text.TextUtils;
224b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
234b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenpublic class CalculatorExpressionBuilder extends SpannableStringBuilder {
244b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
254b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private final CalculatorExpressionTokenizer mTokenizer;
264b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private boolean mIsEdited;
274b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
282be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen    public CalculatorExpressionBuilder(
292be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen            CharSequence text, CalculatorExpressionTokenizer tokenizer, boolean isEdited) {
304b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        super(text);
31741471e3e99760acd44f8536534ac6121af9b03fJustin Klaassen
322be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen        mTokenizer = tokenizer;
33741471e3e99760acd44f8536534ac6121af9b03fJustin Klaassen        mIsEdited = isEdited;
344b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    }
354b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
364b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    @Override
374b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    public SpannableStringBuilder replace(int start, int end, CharSequence tb, int tbstart,
384b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            int tbend) {
394b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        if (start != length() || end != length()) {
404b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            mIsEdited = true;
414b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            return super.replace(start, end, tb, tbstart, tbend);
424b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        }
434b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
444b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        String appendExpr =
454b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mTokenizer.getNormalizedExpression(tb.subSequence(tbstart, tbend).toString());
464b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        if (appendExpr.length() == 1) {
474b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            final String expr = mTokenizer.getNormalizedExpression(toString());
484b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            switch (appendExpr.charAt(0)) {
494b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                case '.':
504b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                    // don't allow two decimals in the same number
514b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                    final int index = expr.lastIndexOf('.');
524b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                    if (index != -1 && TextUtils.isDigitsOnly(expr.substring(index + 1, start))) {
534b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                        appendExpr = "";
544b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                    }
554b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                    break;
564b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                case '+':
574b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                case '*':
584b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                case '/':
594b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                    // don't allow leading operator
604b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                    if (start == 0) {
614b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                        appendExpr = "";
624b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                        break;
634b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                    }
644b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
654b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                    // don't allow multiple successive operators
664b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                    while (start > 0 && "+-*/".indexOf(expr.charAt(start - 1)) != -1) {
674b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                        --start;
684b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                    }
694b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                    // fall through
704b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                case '-':
714b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                    // don't allow -- or +-
724b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                    if (start > 0 && "+-".indexOf(expr.charAt(start - 1)) != -1) {
734b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                        --start;
744b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                    }
754b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
764b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                    // mark as edited since operators can always be appended
774b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                    mIsEdited = true;
784b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                    break;
794b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                default:
804b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                    break;
814b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            }
824b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        }
834b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
844b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        // since this is the first edit replace the entire string
854b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        if (!mIsEdited && appendExpr.length() > 0) {
864b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            start = 0;
874b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            mIsEdited = true;
884b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        }
894b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
904b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        appendExpr = mTokenizer.getLocalizedExpression(appendExpr);
914b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        return super.replace(start, end, appendExpr, 0, appendExpr.length());
924b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    }
934b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen}
94