/* * Catroid: An on-device visual programming system for Android devices * Copyright (C) 2010-2016 The Catrobat Team * (<http://developer.catrobat.org/credits>) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * An additional term exception under section 7 of the GNU Affero * General Public License, version 3, is available at * http://developer.catrobat.org/license_additional_term * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.catrobat.catroid.formulaeditor; import android.content.Context; import android.util.Log; import org.catrobat.catroid.R; import java.util.LinkedList; import java.util.List; public class InternFormula { private static final String TAG = InternFormula.class.getSimpleName(); public static enum CursorTokenPosition { LEFT, MIDDLE, RIGHT } public static enum CursorTokenPropertiesAfterModification { LEFT, RIGHT, SELECT, DO_NOT_MODIFY } public static enum TokenSelectionType { USER_SELECTION, PARSER_ERROR_SELECTION } private ExternInternRepresentationMapping externInternRepresentationMapping; private List<InternToken> internTokenFormulaList; private String externFormulaString; private InternFormulaTokenSelection internFormulaTokenSelection; private int externCursorPosition; private InternToken cursorPositionInternToken; private int cursorPositionInternTokenIndex; private CursorTokenPosition cursorTokenPosition; private InternFormulaParser internTokenFormulaParser; public InternFormula(List<InternToken> internTokenList) { this.internTokenFormulaList = internTokenList; this.externFormulaString = null; this.externInternRepresentationMapping = new ExternInternRepresentationMapping(); this.internFormulaTokenSelection = null; this.externCursorPosition = 0; this.cursorPositionInternTokenIndex = 0; } public InternFormula(List<InternToken> internTokenList, InternFormulaTokenSelection internFormulaTokenSelection, int externCursorPosition) { this.internTokenFormulaList = internTokenList; this.externFormulaString = null; externInternRepresentationMapping = new ExternInternRepresentationMapping(); this.internFormulaTokenSelection = internFormulaTokenSelection; this.externCursorPosition = externCursorPosition; updateInternCursorPosition(); } public void setCursorAndSelection(int externCursorPosition, boolean isSelected) { this.externCursorPosition = externCursorPosition; updateInternCursorPosition(); internFormulaTokenSelection = null; if (isSelected || externInternRepresentationMapping.getInternTokenByExternIndex(externCursorPosition) != ExternInternRepresentationMapping.MAPPING_NOT_FOUND && (getFirstLeftInternToken(externCursorPosition - 1) == cursorPositionInternToken || cursorPositionInternToken .isFunctionParameterBracketOpen()) && ((cursorPositionInternToken.isFunctionName()) || (cursorPositionInternToken.isFunctionParameterBracketOpen() && cursorTokenPosition == CursorTokenPosition.LEFT) || (cursorPositionInternToken.isSensor()) || (cursorPositionInternToken.isUserVariable()) || (cursorPositionInternToken.isUserList()) || (cursorPositionInternToken.isString()))) { selectCursorPositionInternToken(TokenSelectionType.USER_SELECTION); } } public void handleKeyInput(int resourceId, Context context, String name) { List<InternToken> keyInputInternTokenList = new InternFormulaKeyboardAdapter() .createInternTokenListByResourceId(resourceId, name); CursorTokenPropertiesAfterModification cursorTokenPropertiesAfterInput = CursorTokenPropertiesAfterModification.DO_NOT_MODIFY; if (resourceId == R.id.formula_editor_keyboard_delete) { cursorTokenPropertiesAfterInput = handleDeletion(); } else if (isTokenSelected()) { cursorTokenPropertiesAfterInput = replaceSelection(keyInputInternTokenList); } else if (cursorTokenPosition == null) { cursorTokenPropertiesAfterInput = insertRightToCurrentToken(keyInputInternTokenList); } else { switch (cursorTokenPosition) { case LEFT: cursorTokenPropertiesAfterInput = insertLeftToCurrentToken(keyInputInternTokenList); break; case MIDDLE: cursorTokenPropertiesAfterInput = replaceCursorPositionInternTokenByTokenList(keyInputInternTokenList); break; case RIGHT: cursorTokenPropertiesAfterInput = insertRightToCurrentToken(keyInputInternTokenList); break; } } generateExternFormulaStringAndInternExternMapping(context); updateExternCursorPosition(cursorTokenPropertiesAfterInput); updateInternCursorPosition(); } public void updateVariableReferences(String oldName, String newName, Context context) { for (InternToken internToken : internTokenFormulaList) { internToken.updateVariableReferences(oldName, newName); } generateExternFormulaStringAndInternExternMapping(context); } public void getVariableAndListNames(List<String> variables, List<String> lists) { for (InternToken internToken : internTokenFormulaList) { internToken.getVariableAndListNames(variables, lists); } } public void updateCollisionFormula(String oldName, String newName, Context context) { for (InternToken internToken : internTokenFormulaList) { internToken.updateCollisionFormula(oldName, newName); } generateExternFormulaStringAndInternExternMapping(context); } public void updateCollisionFormulaToVersion(Context context, float catroidLanguageVersion) { for (InternToken internToken : internTokenFormulaList) { internToken.updateCollisionFormulaToVersion(catroidLanguageVersion); } generateExternFormulaStringAndInternExternMapping(context); } public void removeVariableReferences(String name, Context context) { LinkedList<InternToken> toRemove = new LinkedList<InternToken>(); for (InternToken internToken : internTokenFormulaList) { if (internToken.isUserVariable(name)) { toRemove.add(internToken); } } for (InternToken internToken : toRemove) { internTokenFormulaList.remove(internToken); } generateExternFormulaStringAndInternExternMapping(context); } public void updateInternCursorPosition() { int cursorPositionTokenIndex = externInternRepresentationMapping .getInternTokenByExternIndex(externCursorPosition); int leftCursorPositionTokenIndex = externInternRepresentationMapping .getInternTokenByExternIndex(externCursorPosition - 1); int leftleftCursorPositionTokenIndex = externInternRepresentationMapping .getInternTokenByExternIndex(externCursorPosition - 2); if (cursorPositionTokenIndex != ExternInternRepresentationMapping.MAPPING_NOT_FOUND) { if (leftCursorPositionTokenIndex != ExternInternRepresentationMapping.MAPPING_NOT_FOUND && cursorPositionTokenIndex == leftCursorPositionTokenIndex) { cursorTokenPosition = CursorTokenPosition.MIDDLE; } else { cursorTokenPosition = CursorTokenPosition.LEFT; } } else if (leftCursorPositionTokenIndex != ExternInternRepresentationMapping.MAPPING_NOT_FOUND) { cursorTokenPosition = CursorTokenPosition.RIGHT; } else if (leftleftCursorPositionTokenIndex != ExternInternRepresentationMapping.MAPPING_NOT_FOUND) { cursorTokenPosition = CursorTokenPosition.RIGHT; leftCursorPositionTokenIndex = leftleftCursorPositionTokenIndex; } else { cursorTokenPosition = null; this.cursorPositionInternToken = null; return; } switch (cursorTokenPosition) { case LEFT: this.cursorPositionInternToken = internTokenFormulaList.get(cursorPositionTokenIndex); this.cursorPositionInternTokenIndex = cursorPositionTokenIndex; break; case MIDDLE: this.cursorPositionInternToken = internTokenFormulaList.get(cursorPositionTokenIndex); this.cursorPositionInternTokenIndex = cursorPositionTokenIndex; break; case RIGHT: this.cursorPositionInternToken = internTokenFormulaList.get(leftCursorPositionTokenIndex); this.cursorPositionInternTokenIndex = leftCursorPositionTokenIndex; break; } } private void updateExternCursorPosition(CursorTokenPropertiesAfterModification cursorTokenPropertiesAfterInput) { switch (cursorTokenPropertiesAfterInput) { case LEFT: setExternCursorPositionLeftTo(cursorPositionInternTokenIndex); break; case RIGHT: setExternCursorPositionRightTo(cursorPositionInternTokenIndex); break; } } private CursorTokenPropertiesAfterModification replaceSelection(List<InternToken> tokenListToInsert) { if (InternFormulaUtils.isPeriodToken(tokenListToInsert)) { tokenListToInsert = new LinkedList<InternToken>(); tokenListToInsert.add(new InternToken(InternTokenType.NUMBER, "0.")); } int internTokenSelectionStart = internFormulaTokenSelection.getStartIndex(); int internTokenSelectionEnd = internFormulaTokenSelection.getEndIndex(); if (internTokenSelectionStart > internTokenSelectionEnd || internTokenSelectionStart < 0 || internTokenSelectionEnd < 0) { internFormulaTokenSelection = null; return CursorTokenPropertiesAfterModification.DO_NOT_MODIFY; } List<InternToken> tokenListToRemove = new LinkedList<InternToken>(); for (int tokensToRemove = 0; tokensToRemove <= internTokenSelectionEnd - internTokenSelectionStart; tokensToRemove++) { tokenListToRemove.add(internTokenFormulaList.get(internTokenSelectionStart + tokensToRemove)); } if (InternFormulaUtils.isFunction(tokenListToRemove)) { cursorPositionInternToken = tokenListToRemove.get(0); cursorPositionInternTokenIndex = internTokenSelectionStart; return replaceCursorPositionInternTokenByTokenList(tokenListToInsert); } else { replaceInternTokens(tokenListToInsert, internTokenSelectionStart, internTokenSelectionEnd); return setCursorPositionAndSelectionAfterInput(internTokenSelectionStart); } } private void deleteInternTokens(int deleteIndexStart, int deleteIndexEnd) { List<InternToken> tokenListToInsert = new LinkedList<InternToken>(); replaceInternTokens(tokenListToInsert, deleteIndexStart, deleteIndexEnd); } private void replaceInternTokens(List<InternToken> tokenListToInsert, int replaceIndexStart, int replaceIndexEnd) { for (int tokensToRemove = replaceIndexEnd - replaceIndexStart; tokensToRemove >= 0; tokensToRemove--) { internTokenFormulaList.remove(replaceIndexStart); } internTokenFormulaList.addAll(replaceIndexStart, tokenListToInsert); } private CursorTokenPropertiesAfterModification handleDeletion() { CursorTokenPropertiesAfterModification cursorTokenPropertiesAfterModification = CursorTokenPropertiesAfterModification.DO_NOT_MODIFY; if (internFormulaTokenSelection != null) { deleteInternTokens(internFormulaTokenSelection.getStartIndex(), internFormulaTokenSelection.getEndIndex()); cursorPositionInternTokenIndex = internFormulaTokenSelection.getStartIndex(); cursorPositionInternToken = null; internFormulaTokenSelection = null; cursorTokenPropertiesAfterModification = CursorTokenPropertiesAfterModification.LEFT; } else { switch (cursorTokenPosition) { case LEFT: InternToken firstLeftInternToken = getFirstLeftInternToken(externCursorPosition - 1); if (firstLeftInternToken == null) { cursorTokenPropertiesAfterModification = CursorTokenPropertiesAfterModification.DO_NOT_MODIFY; } else { if (firstLeftInternToken.getInternTokenType() == InternTokenType.FUNCTION_PARAMETER_DELIMITER) { setExternCursorPositionLeftTo(internTokenFormulaList.indexOf(firstLeftInternToken)); break; } int firstLeftInternTokenIndex = internTokenFormulaList.indexOf(firstLeftInternToken); cursorTokenPropertiesAfterModification = deleteInternTokenByIndex(firstLeftInternTokenIndex); } break; case MIDDLE: cursorTokenPropertiesAfterModification = deleteInternTokenByIndex(cursorPositionInternTokenIndex); break; case RIGHT: InternToken internToken = getFirstLeftInternToken(externCursorPosition); if (internToken.getInternTokenType() == InternTokenType.FUNCTION_PARAMETER_DELIMITER) { setExternCursorPositionLeftTo(internTokenFormulaList.indexOf(internToken)); break; } cursorTokenPropertiesAfterModification = deleteInternTokenByIndex(cursorPositionInternTokenIndex); break; } } return cursorTokenPropertiesAfterModification; } private CursorTokenPropertiesAfterModification deleteInternTokenByIndex(int internTokenIndex) { InternToken tokenToDelete = internTokenFormulaList.get(internTokenIndex); switch (tokenToDelete.getInternTokenType()) { case NUMBER: int externNumberOffset = externInternRepresentationMapping.getExternTokenStartOffset( externCursorPosition, internTokenIndex); if (externNumberOffset == -1) { return CursorTokenPropertiesAfterModification.DO_NOT_MODIFY; } InternToken modifiedToken = InternFormulaUtils.deleteNumberByOffset(tokenToDelete, externNumberOffset); if (modifiedToken == null) { internTokenFormulaList.remove(internTokenIndex); cursorPositionInternTokenIndex = internTokenIndex; cursorPositionInternToken = null; return CursorTokenPropertiesAfterModification.LEFT; } externCursorPosition--; return CursorTokenPropertiesAfterModification.DO_NOT_MODIFY; case FUNCTION_NAME: List<InternToken> functionInternTokens = InternFormulaUtils.getFunctionByName(internTokenFormulaList, internTokenIndex); if (functionInternTokens == null || functionInternTokens.size() == 0) { return CursorTokenPropertiesAfterModification.DO_NOT_MODIFY; } int lastListIndex = functionInternTokens.size() - 1; InternToken lastFunctionToken = functionInternTokens.get(lastListIndex); int endIndexToDelete = internTokenFormulaList.indexOf(lastFunctionToken); deleteInternTokens(internTokenIndex, endIndexToDelete); setExternCursorPositionLeftTo(internTokenIndex); cursorPositionInternTokenIndex = internTokenIndex; cursorPositionInternToken = null; return CursorTokenPropertiesAfterModification.LEFT; case FUNCTION_PARAMETERS_BRACKET_OPEN: functionInternTokens = InternFormulaUtils.getFunctionByFunctionBracketOpen(internTokenFormulaList, internTokenIndex); if (functionInternTokens == null || functionInternTokens.size() == 0) { return CursorTokenPropertiesAfterModification.DO_NOT_MODIFY; } int functionInternTokensLastIndex = functionInternTokens.size() - 1; int startDeletionIndex = internTokenFormulaList.indexOf(functionInternTokens.get(0)); endIndexToDelete = internTokenFormulaList.indexOf(functionInternTokens .get(functionInternTokensLastIndex)); deleteInternTokens(startDeletionIndex, endIndexToDelete); cursorPositionInternTokenIndex = startDeletionIndex; cursorPositionInternToken = null; return CursorTokenPropertiesAfterModification.LEFT; case FUNCTION_PARAMETERS_BRACKET_CLOSE: functionInternTokens = InternFormulaUtils.getFunctionByFunctionBracketClose(internTokenFormulaList, internTokenIndex); if (functionInternTokens == null || functionInternTokens.size() == 0) { return CursorTokenPropertiesAfterModification.DO_NOT_MODIFY; } functionInternTokensLastIndex = functionInternTokens.size() - 1; startDeletionIndex = internTokenFormulaList.indexOf(functionInternTokens.get(0)); endIndexToDelete = internTokenFormulaList.indexOf(functionInternTokens .get(functionInternTokensLastIndex)); deleteInternTokens(startDeletionIndex, endIndexToDelete); cursorPositionInternTokenIndex = startDeletionIndex; cursorPositionInternToken = null; return CursorTokenPropertiesAfterModification.LEFT; case FUNCTION_PARAMETER_DELIMITER: functionInternTokens = InternFormulaUtils.getFunctionByParameterDelimiter(internTokenFormulaList, internTokenIndex); if (functionInternTokens == null || functionInternTokens.size() == 0) { return CursorTokenPropertiesAfterModification.DO_NOT_MODIFY; } functionInternTokensLastIndex = functionInternTokens.size() - 1; startDeletionIndex = internTokenFormulaList.indexOf(functionInternTokens.get(0)); endIndexToDelete = internTokenFormulaList.indexOf(functionInternTokens .get(functionInternTokensLastIndex)); deleteInternTokens(startDeletionIndex, endIndexToDelete); cursorPositionInternTokenIndex = startDeletionIndex; cursorPositionInternToken = null; return CursorTokenPropertiesAfterModification.LEFT; default: deleteInternTokens(internTokenIndex, internTokenIndex); cursorPositionInternTokenIndex = internTokenIndex; cursorPositionInternToken = null; return CursorTokenPropertiesAfterModification.LEFT; } } private void setExternCursorPositionLeftTo(int internTokenIndex) { if (internTokenFormulaList.size() < 1) { externCursorPosition = 1; return; } if (internTokenIndex >= internTokenFormulaList.size()) { setExternCursorPositionRightTo(internTokenFormulaList.size() - 1); return; } int externTokenStartIndex = externInternRepresentationMapping.getExternTokenStartIndex(internTokenIndex); if (externTokenStartIndex == ExternInternRepresentationMapping.MAPPING_NOT_FOUND) { return; } externCursorPosition = externTokenStartIndex; cursorTokenPosition = CursorTokenPosition.LEFT; } private void setExternCursorPositionRightTo(int internTokenIndex) { if (internTokenFormulaList.size() < 1) { return; } if (internTokenIndex >= internTokenFormulaList.size()) { internTokenIndex = internTokenFormulaList.size() - 1; } int externTokenEndIndex = externInternRepresentationMapping.getExternTokenEndIndex(internTokenIndex); if (externTokenEndIndex == ExternInternRepresentationMapping.MAPPING_NOT_FOUND) { return; } externCursorPosition = externTokenEndIndex; cursorTokenPosition = CursorTokenPosition.RIGHT; } public void generateExternFormulaStringAndInternExternMapping(Context context) { InternToExternGenerator internToExternGenerator = new InternToExternGenerator(context); internToExternGenerator.generateExternStringAndMapping(internTokenFormulaList); externFormulaString = internToExternGenerator.getGeneratedExternFormulaString(); externInternRepresentationMapping = internToExternGenerator.getGeneratedExternInternRepresentationMapping(); } public String trimExternFormulaString(Context context) { InternToExternGenerator internToExternGenerator = new InternToExternGenerator(context); internToExternGenerator.trimExternString(internTokenFormulaList); externFormulaString = internToExternGenerator.getGeneratedExternFormulaString(); externInternRepresentationMapping = internToExternGenerator.getGeneratedExternInternRepresentationMapping(); return externFormulaString; } private void selectCursorPositionInternToken(TokenSelectionType internTokenSelectionType) { internFormulaTokenSelection = null; if (cursorPositionInternToken == null) { return; } switch (cursorPositionInternToken.getInternTokenType()) { case FUNCTION_NAME: List<InternToken> functionInternTokens = InternFormulaUtils.getFunctionByName(internTokenFormulaList, cursorPositionInternTokenIndex); if (functionInternTokens == null || functionInternTokens.size() == 0) { return; } int lastListIndex = functionInternTokens.size() - 1; InternToken lastFunctionToken = functionInternTokens.get(lastListIndex); int endSelectionIndex = internTokenFormulaList.indexOf(lastFunctionToken); internFormulaTokenSelection = new InternFormulaTokenSelection(internTokenSelectionType, cursorPositionInternTokenIndex, endSelectionIndex); break; case FUNCTION_PARAMETERS_BRACKET_OPEN: functionInternTokens = InternFormulaUtils.getFunctionByFunctionBracketOpen(internTokenFormulaList, cursorPositionInternTokenIndex); if (functionInternTokens == null || functionInternTokens.size() == 0) { return; } int functionInternTokensLastIndex = functionInternTokens.size() - 1; int startSelectionIndex = internTokenFormulaList.indexOf(functionInternTokens.get(0)); endSelectionIndex = internTokenFormulaList.indexOf(functionInternTokens .get(functionInternTokensLastIndex)); internFormulaTokenSelection = new InternFormulaTokenSelection(internTokenSelectionType, startSelectionIndex, endSelectionIndex); break; case FUNCTION_PARAMETERS_BRACKET_CLOSE: functionInternTokens = InternFormulaUtils.getFunctionByFunctionBracketClose(internTokenFormulaList, cursorPositionInternTokenIndex); if (functionInternTokens == null || functionInternTokens.size() == 0) { return; } functionInternTokensLastIndex = functionInternTokens.size() - 1; startSelectionIndex = internTokenFormulaList.indexOf(functionInternTokens.get(0)); endSelectionIndex = internTokenFormulaList.indexOf(functionInternTokens .get(functionInternTokensLastIndex)); internFormulaTokenSelection = new InternFormulaTokenSelection(internTokenSelectionType, startSelectionIndex, endSelectionIndex); break; case FUNCTION_PARAMETER_DELIMITER: functionInternTokens = InternFormulaUtils.getFunctionByParameterDelimiter(internTokenFormulaList, cursorPositionInternTokenIndex); if (functionInternTokens == null || functionInternTokens.size() == 0) { return; } functionInternTokensLastIndex = functionInternTokens.size() - 1; startSelectionIndex = internTokenFormulaList.indexOf(functionInternTokens.get(0)); endSelectionIndex = internTokenFormulaList.indexOf(functionInternTokens .get(functionInternTokensLastIndex)); internFormulaTokenSelection = new InternFormulaTokenSelection(internTokenSelectionType, startSelectionIndex, endSelectionIndex); break; case BRACKET_OPEN: List<InternToken> bracketsInternTokens = InternFormulaUtils.generateTokenListByBracketOpen( internTokenFormulaList, cursorPositionInternTokenIndex); if (bracketsInternTokens == null || bracketsInternTokens.size() == 0) { return; } int bracketsInternTokensLastIndex = bracketsInternTokens.size() - 1; startSelectionIndex = cursorPositionInternTokenIndex; endSelectionIndex = internTokenFormulaList.indexOf(bracketsInternTokens .get(bracketsInternTokensLastIndex)); internFormulaTokenSelection = new InternFormulaTokenSelection(internTokenSelectionType, startSelectionIndex, endSelectionIndex); break; case BRACKET_CLOSE: bracketsInternTokens = InternFormulaUtils.generateTokenListByBracketClose(internTokenFormulaList, cursorPositionInternTokenIndex); if (bracketsInternTokens == null || bracketsInternTokens.size() == 0) { return; } bracketsInternTokensLastIndex = bracketsInternTokens.size() - 1; startSelectionIndex = internTokenFormulaList.indexOf(bracketsInternTokens.get(0)); endSelectionIndex = internTokenFormulaList.indexOf(bracketsInternTokens .get(bracketsInternTokensLastIndex)); internFormulaTokenSelection = new InternFormulaTokenSelection(internTokenSelectionType, startSelectionIndex, endSelectionIndex); break; default: internFormulaTokenSelection = new InternFormulaTokenSelection(internTokenSelectionType, cursorPositionInternTokenIndex, cursorPositionInternTokenIndex); break; } } private CursorTokenPropertiesAfterModification insertLeftToCurrentToken(List<InternToken> internTokensToInsert) { InternToken firstLeftInternToken = null; if (cursorPositionInternTokenIndex > 0) { firstLeftInternToken = internTokenFormulaList.get(cursorPositionInternTokenIndex - 1); } if (cursorPositionInternToken.isNumber() && InternFormulaUtils.isNumberToken(internTokensToInsert)) { String numberToInsert = internTokensToInsert.get(0).getTokenStringValue(); InternFormulaUtils.insertIntoNumberToken(cursorPositionInternToken, 0, numberToInsert); externCursorPosition++; return CursorTokenPropertiesAfterModification.DO_NOT_MODIFY; } if (cursorPositionInternToken.isNumber() && InternFormulaUtils.isPeriodToken(internTokensToInsert)) { String numberString = cursorPositionInternToken.getTokenStringValue(); if (numberString.contains(".")) { return CursorTokenPropertiesAfterModification.DO_NOT_MODIFY; } InternFormulaUtils.insertIntoNumberToken(cursorPositionInternToken, 0, "0."); externCursorPosition += 2; return CursorTokenPropertiesAfterModification.DO_NOT_MODIFY; } if (firstLeftInternToken != null && firstLeftInternToken.isNumber() && InternFormulaUtils.isNumberToken(internTokensToInsert)) { firstLeftInternToken.appendToTokenStringValue(internTokensToInsert); return CursorTokenPropertiesAfterModification.DO_NOT_MODIFY; } if (firstLeftInternToken != null && firstLeftInternToken.isNumber() && InternFormulaUtils.isPeriodToken(internTokensToInsert)) { String numberString = firstLeftInternToken.getTokenStringValue(); if (numberString.contains(".")) { return CursorTokenPropertiesAfterModification.DO_NOT_MODIFY; } firstLeftInternToken.appendToTokenStringValue("."); return CursorTokenPropertiesAfterModification.DO_NOT_MODIFY; } if (InternFormulaUtils.isPeriodToken(internTokensToInsert)) { internTokenFormulaList.add(cursorPositionInternTokenIndex, new InternToken(InternTokenType.NUMBER, "0.")); cursorPositionInternToken = null; return CursorTokenPropertiesAfterModification.RIGHT; } internTokenFormulaList.addAll(cursorPositionInternTokenIndex, internTokensToInsert); return setCursorPositionAndSelectionAfterInput(cursorPositionInternTokenIndex); } private CursorTokenPropertiesAfterModification insertRightToCurrentToken(List<InternToken> internTokensToInsert) { if (cursorPositionInternToken == null) { if (InternFormulaUtils.isPeriodToken(internTokensToInsert)) { internTokensToInsert = new LinkedList<InternToken>(); internTokensToInsert.add(new InternToken(InternTokenType.NUMBER, "0.")); } internTokenFormulaList.addAll(0, internTokensToInsert); return setCursorPositionAndSelectionAfterInput(0); } if (cursorPositionInternToken.isNumber() && InternFormulaUtils.isNumberToken(internTokensToInsert)) { cursorPositionInternToken.appendToTokenStringValue(internTokensToInsert); return CursorTokenPropertiesAfterModification.RIGHT; } if (cursorPositionInternToken.isNumber() && InternFormulaUtils.isPeriodToken(internTokensToInsert)) { String numberString = cursorPositionInternToken.getTokenStringValue(); if (numberString.contains(".")) { return CursorTokenPropertiesAfterModification.DO_NOT_MODIFY; } cursorPositionInternToken.appendToTokenStringValue("."); return CursorTokenPropertiesAfterModification.RIGHT; } if (InternFormulaUtils.isPeriodToken(internTokensToInsert)) { internTokenFormulaList.add(cursorPositionInternTokenIndex + 1, new InternToken(InternTokenType.NUMBER, "0.")); cursorPositionInternToken = null; cursorPositionInternTokenIndex = cursorPositionInternTokenIndex + 1; return CursorTokenPropertiesAfterModification.RIGHT; } internTokenFormulaList.addAll(cursorPositionInternTokenIndex + 1, internTokensToInsert); return setCursorPositionAndSelectionAfterInput(cursorPositionInternTokenIndex + 1); } private CursorTokenPropertiesAfterModification setCursorPositionAndSelectionAfterInput(int insertedInternTokenIndex) { InternToken insertedInternToken = internTokenFormulaList.get(insertedInternTokenIndex); if (insertedInternToken.getInternTokenType() == InternTokenType.FUNCTION_NAME) { List<InternToken> functionInternTokenList = InternFormulaUtils.getFunctionByName( internTokenFormulaList, insertedInternTokenIndex); if (functionInternTokenList.size() >= 4) { List<List<InternToken>> functionParameters = InternFormulaUtils .getFunctionParameterInternTokensAsLists(functionInternTokenList); List<InternToken> functionFirstParameter = functionParameters.get(0); internFormulaTokenSelection = new InternFormulaTokenSelection(TokenSelectionType.USER_SELECTION, insertedInternTokenIndex + 2, insertedInternTokenIndex + functionFirstParameter.size() + 1); cursorPositionInternTokenIndex = internFormulaTokenSelection.getEndIndex(); } else { cursorPositionInternTokenIndex = insertedInternTokenIndex + functionInternTokenList.size() - 1; internFormulaTokenSelection = null; } } else { cursorPositionInternTokenIndex = insertedInternTokenIndex; internFormulaTokenSelection = null; } cursorPositionInternToken = null; return CursorTokenPropertiesAfterModification.RIGHT; } private CursorTokenPropertiesAfterModification replaceCursorPositionInternTokenByTokenList( List<InternToken> internTokensToReplaceWith) { Log.i(TAG, "replaceCursorPositionInternTokenByTokenList:enter"); if (cursorPositionInternToken.isNumber() && internTokensToReplaceWith.size() == 1 && internTokensToReplaceWith.get(0).isOperator()) { int externNumberOffset = externInternRepresentationMapping.getExternTokenStartOffset(externCursorPosition, cursorPositionInternTokenIndex); List<InternToken> replaceList = InternFormulaUtils.insertOperatorToNumberToken(cursorPositionInternToken, externNumberOffset, internTokensToReplaceWith.get(0)); replaceInternTokens(replaceList, cursorPositionInternTokenIndex, cursorPositionInternTokenIndex); return setCursorPositionAndSelectionAfterInput(cursorPositionInternTokenIndex); } if (cursorPositionInternToken.isNumber() && InternFormulaUtils.isNumberToken(internTokensToReplaceWith)) { InternToken numberTokenToInsert = internTokensToReplaceWith.get(0); int externNumberOffset = externInternRepresentationMapping.getExternTokenStartOffset(externCursorPosition, cursorPositionInternTokenIndex); if (externNumberOffset == -1) { return CursorTokenPropertiesAfterModification.DO_NOT_MODIFY; } InternFormulaUtils.insertIntoNumberToken(cursorPositionInternToken, externNumberOffset, numberTokenToInsert.getTokenStringValue()); externCursorPosition++; return CursorTokenPropertiesAfterModification.DO_NOT_MODIFY; } if (cursorPositionInternToken.isNumber() && InternFormulaUtils.isPeriodToken(internTokensToReplaceWith)) { String numberString = cursorPositionInternToken.getTokenStringValue(); if (numberString.contains(".")) { return CursorTokenPropertiesAfterModification.DO_NOT_MODIFY; } int externNumberOffset = externInternRepresentationMapping.getExternTokenStartOffset(externCursorPosition, cursorPositionInternTokenIndex); if (externNumberOffset == -1) { return CursorTokenPropertiesAfterModification.DO_NOT_MODIFY; } InternFormulaUtils.insertIntoNumberToken(cursorPositionInternToken, externNumberOffset, "."); externCursorPosition++; return CursorTokenPropertiesAfterModification.DO_NOT_MODIFY; } if (cursorPositionInternToken.isFunctionName()) { List<InternToken> functionInternTokens = InternFormulaUtils.getFunctionByName(internTokenFormulaList, cursorPositionInternTokenIndex); if (functionInternTokens == null) { return CursorTokenPropertiesAfterModification.DO_NOT_MODIFY; } int lastListIndex = functionInternTokens.size() - 1; InternToken lastFunctionToken = functionInternTokens.get(lastListIndex); int endIndexToReplace = internTokenFormulaList.indexOf(lastFunctionToken); List<InternToken> tokensToInsert = InternFormulaUtils.replaceFunctionByTokens(functionInternTokens, internTokensToReplaceWith); replaceInternTokens(tokensToInsert, cursorPositionInternTokenIndex, endIndexToReplace); return setCursorPositionAndSelectionAfterInput(cursorPositionInternTokenIndex); } if (InternFormulaUtils.isPeriodToken(internTokensToReplaceWith)) { internTokenFormulaList.add(cursorPositionInternTokenIndex + 1, new InternToken(InternTokenType.NUMBER, "0.")); cursorPositionInternToken = null; cursorPositionInternTokenIndex = cursorPositionInternTokenIndex + 1; return CursorTokenPropertiesAfterModification.RIGHT; } replaceInternTokens(internTokensToReplaceWith, cursorPositionInternTokenIndex, cursorPositionInternTokenIndex); return setCursorPositionAndSelectionAfterInput(cursorPositionInternTokenIndex); } public InternToken getFirstLeftInternToken(int externIndex) { for (int searchIndex = externIndex; searchIndex >= 0; searchIndex--) { if (externInternRepresentationMapping.getInternTokenByExternIndex(searchIndex) != ExternInternRepresentationMapping.MAPPING_NOT_FOUND) { int internTokenIndex = externInternRepresentationMapping.getInternTokenByExternIndex(searchIndex); return internTokenFormulaList.get(internTokenIndex); } } return null; } public int getExternCursorPosition() { return this.externCursorPosition; } public InternFormulaParser getInternFormulaParser() { internTokenFormulaParser = new InternFormulaParser(internTokenFormulaList); return internTokenFormulaParser; } public void selectParseErrorTokenAndSetCursor() { if (internTokenFormulaParser == null || internTokenFormulaList.size() == 0) { return; } int internErrorTokenIndex = internTokenFormulaParser.getErrorTokenIndex(); if (internErrorTokenIndex < 0) { return; } if (internErrorTokenIndex >= internTokenFormulaList.size()) { internErrorTokenIndex = internTokenFormulaList.size() - 1; } setExternCursorPositionRightTo(internErrorTokenIndex); cursorPositionInternTokenIndex = internErrorTokenIndex; cursorPositionInternToken = internTokenFormulaList.get(cursorPositionInternTokenIndex); selectCursorPositionInternToken(TokenSelectionType.PARSER_ERROR_SELECTION); } public TokenSelectionType getExternSelectionType() { if (!isTokenSelected()) { return null; } return internFormulaTokenSelection.getTokenSelectionType(); } public void selectWholeFormula() { if (internTokenFormulaList.size() == 0) { return; } internFormulaTokenSelection = new InternFormulaTokenSelection(TokenSelectionType.USER_SELECTION, 0, internTokenFormulaList.size() - 1); } public InternFormulaState getInternFormulaState() { List<InternToken> deepCopyOfInternTokenFormula = new LinkedList<InternToken>(); InternFormulaTokenSelection deepCopyOfInternFormulaTokenSelection = null; for (InternToken tokenToCopy : internTokenFormulaList) { deepCopyOfInternTokenFormula.add(tokenToCopy.deepCopy()); } if (isTokenSelected()) { deepCopyOfInternFormulaTokenSelection = internFormulaTokenSelection.deepCopy(); } return new InternFormulaState(deepCopyOfInternTokenFormula, deepCopyOfInternFormulaTokenSelection, externCursorPosition); } public InternFormulaTokenSelection getSelection() { return internFormulaTokenSelection; } public int getExternSelectionStartIndex() { if (internFormulaTokenSelection == null) { return -1; } int externSelectionStartIndex = externInternRepresentationMapping .getExternTokenStartIndex(internFormulaTokenSelection.getStartIndex()); if (externSelectionStartIndex == ExternInternRepresentationMapping.MAPPING_NOT_FOUND) { return -1; } return externSelectionStartIndex; } public int getExternSelectionEndIndex() { if (internFormulaTokenSelection == null) { return -1; } int externSelectionEndIndex = externInternRepresentationMapping .getExternTokenEndIndex(internFormulaTokenSelection.getEndIndex()); if (externSelectionEndIndex == ExternInternRepresentationMapping.MAPPING_NOT_FOUND) { return -1; } return externSelectionEndIndex; } private InternToken getSelectedToken() { if (internFormulaTokenSelection == null || internFormulaTokenSelection.getTokenSelectionType() != TokenSelectionType.USER_SELECTION) { return null; } int currentIndex = 0; for (InternToken token : internTokenFormulaList) { if (token.getInternTokenType() == InternTokenType.STRING && internFormulaTokenSelection.getStartIndex() == currentIndex) { return token; } currentIndex++; } return null; } public String getSelectedText() { InternToken token = getSelectedToken(); if (token == null) { return null; } return token.getTokenStringValue(); } public void overrideSelectedText(String string, Context context) { InternToken token = getSelectedToken(); if (token == null) { return; } token.setTokenStringValue(string); generateExternFormulaStringAndInternExternMapping(context); } public String getExternFormulaString() { return externFormulaString; } private boolean isTokenSelected() { return internFormulaTokenSelection != null; } public boolean isThereSomethingToDelete() { if (internFormulaTokenSelection != null) { return true; } return !(cursorTokenPosition == null || (cursorTokenPosition == CursorTokenPosition.LEFT && getFirstLeftInternToken(externCursorPosition - 1) == null)); } }