/*
* 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.res.Resources;
import android.util.Log;
import org.catrobat.catroid.ProjectManager;
import org.catrobat.catroid.bluetooth.base.BluetoothDevice;
import org.catrobat.catroid.common.CatroidService;
import org.catrobat.catroid.common.Constants;
import org.catrobat.catroid.common.LookData;
import org.catrobat.catroid.common.ServiceProvider;
import org.catrobat.catroid.content.GroupSprite;
import org.catrobat.catroid.content.Look;
import org.catrobat.catroid.content.Sprite;
import org.catrobat.catroid.content.bricks.Brick;
import org.catrobat.catroid.devices.arduino.Arduino;
import org.catrobat.catroid.devices.raspberrypi.RPiSocketConnection;
import org.catrobat.catroid.devices.raspberrypi.RaspberryPiService;
import org.catrobat.catroid.nfc.NfcHandler;
import org.catrobat.catroid.sensing.CollisionDetection;
import org.catrobat.catroid.stage.StageActivity;
import org.catrobat.catroid.utils.TouchUtil;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class FormulaElement implements Serializable {
private static final long serialVersionUID = 1L;
public static enum ElementType {
OPERATOR, FUNCTION, NUMBER, SENSOR, USER_VARIABLE, USER_LIST, BRACKET, STRING, COLLISION_FORMULA
}
public static final Double NOT_EXISTING_USER_VARIABLE_INTERPRETATION_VALUE = 0d;
public static final Double NOT_EXISTING_USER_LIST_INTERPRETATION_VALUE = 0d;
private ElementType type;
private String value;
private FormulaElement leftChild = null;
private FormulaElement rightChild = null;
private transient FormulaElement parent = null;
public FormulaElement(ElementType type, String value, FormulaElement parent) {
this.type = type;
this.value = value;
this.parent = parent;
}
public FormulaElement(ElementType type, String value, FormulaElement parent, FormulaElement leftChild,
FormulaElement rightChild) {
this.type = type;
this.value = value;
this.parent = parent;
this.leftChild = leftChild;
this.rightChild = rightChild;
if (leftChild != null) {
this.leftChild.parent = this;
}
if (rightChild != null) {
this.rightChild.parent = this;
}
}
public ElementType getElementType() {
return type;
}
public String getValue() {
return value;
}
public List<InternToken> getInternTokenList() {
List<InternToken> internTokenList = new LinkedList<InternToken>();
switch (type) {
case BRACKET:
internTokenList.add(new InternToken(InternTokenType.BRACKET_OPEN));
if (rightChild != null) {
internTokenList.addAll(rightChild.getInternTokenList());
}
internTokenList.add(new InternToken(InternTokenType.BRACKET_CLOSE));
break;
case OPERATOR:
if (leftChild != null) {
internTokenList.addAll(leftChild.getInternTokenList());
}
internTokenList.add(new InternToken(InternTokenType.OPERATOR, this.value));
if (rightChild != null) {
internTokenList.addAll(rightChild.getInternTokenList());
}
break;
case FUNCTION:
internTokenList.add(new InternToken(InternTokenType.FUNCTION_NAME, value));
boolean functionHasParameters = false;
if (leftChild != null) {
internTokenList.add(new InternToken(InternTokenType.FUNCTION_PARAMETERS_BRACKET_OPEN));
functionHasParameters = true;
internTokenList.addAll(leftChild.getInternTokenList());
}
if (rightChild != null) {
internTokenList.add(new InternToken(InternTokenType.FUNCTION_PARAMETER_DELIMITER));
internTokenList.addAll(rightChild.getInternTokenList());
}
if (functionHasParameters) {
internTokenList.add(new InternToken(InternTokenType.FUNCTION_PARAMETERS_BRACKET_CLOSE));
}
break;
case USER_VARIABLE:
internTokenList.add(new InternToken(InternTokenType.USER_VARIABLE, this.value));
break;
case USER_LIST:
internTokenList.add(new InternToken(InternTokenType.USER_LIST, this.value));
break;
case NUMBER:
internTokenList.add(new InternToken(InternTokenType.NUMBER, this.value));
break;
case SENSOR:
internTokenList.add(new InternToken(InternTokenType.SENSOR, this.value));
break;
case STRING:
internTokenList.add(new InternToken(InternTokenType.STRING, value));
break;
case COLLISION_FORMULA:
internTokenList.add(new InternToken(InternTokenType.COLLISION_FORMULA, this.value));
break;
}
return internTokenList;
}
public FormulaElement getRoot() {
FormulaElement root = this;
while (root.getParent() != null) {
root = root.getParent();
}
return root;
}
public void updateVariableReferences(String oldName, String newName) {
if (leftChild != null) {
leftChild.updateVariableReferences(oldName, newName);
}
if (rightChild != null) {
rightChild.updateVariableReferences(oldName, newName);
}
if (type == ElementType.USER_VARIABLE && value.equals(oldName)) {
value = newName;
}
}
public void getVariableAndListNames(List<String> variables, List<String> lists) {
if (leftChild != null) {
leftChild.getVariableAndListNames(variables, lists);
}
if (rightChild != null) {
rightChild.getVariableAndListNames(variables, lists);
}
if (type == ElementType.USER_VARIABLE && !variables.contains(value)) {
variables.add(value);
}
if (type == ElementType.USER_LIST && !lists.contains(value)) {
lists.add(value);
}
}
public boolean containsSpriteInCollision(String name) {
boolean contained = false;
if (leftChild != null) {
contained |= leftChild.containsSpriteInCollision(name);
}
if (rightChild != null) {
contained |= rightChild.containsSpriteInCollision(name);
}
if (type == ElementType.COLLISION_FORMULA && value.equals(name)) {
contained = true;
}
return contained;
}
public void updateCollisionFormula(String oldName, String newName) {
if (leftChild != null) {
leftChild.updateCollisionFormula(oldName, newName);
}
if (rightChild != null) {
rightChild.updateCollisionFormula(oldName, newName);
}
if (type == ElementType.COLLISION_FORMULA && value.equals(oldName)) {
value = newName;
}
}
public void updateCollisionFormulaToVersion(float catroidLanguageVersion) {
if (catroidLanguageVersion == 0.993f) {
if (leftChild != null) {
leftChild.updateCollisionFormulaToVersion(catroidLanguageVersion);
}
if (rightChild != null) {
rightChild.updateCollisionFormulaToVersion(catroidLanguageVersion);
}
if (type == ElementType.COLLISION_FORMULA) {
String secondSpriteName = CollisionDetection.getSecondSpriteNameFromCollisionFormulaString(value);
if (secondSpriteName != null) {
value = secondSpriteName;
}
}
}
}
public Object interpretRecursive(Sprite sprite) {
Object returnValue = 0d;
switch (type) {
case BRACKET:
returnValue = rightChild.interpretRecursive(sprite);
break;
case NUMBER:
returnValue = value;
break;
case OPERATOR:
Operators operator = Operators.getOperatorByValue(value);
returnValue = interpretOperator(operator, sprite);
break;
case FUNCTION:
Functions function = Functions.getFunctionByValue(value);
returnValue = interpretFunction(function, sprite);
break;
case SENSOR:
returnValue = interpretSensor(sprite);
break;
case USER_VARIABLE:
returnValue = interpretUserVariable(sprite);
break;
case USER_LIST:
returnValue = interpretUserList(sprite);
break;
case STRING:
returnValue = value;
break;
case COLLISION_FORMULA:
try {
returnValue = interpretCollision(sprite, value);
} catch (Exception exception) {
returnValue = 0d;
Log.e(getClass().getSimpleName(), Log.getStackTraceString(exception));
}
}
return normalizeDegeneratedDoubleValues(returnValue);
}
private Object interpretCollision(Sprite firstSprite, String formula) {
String secondSpriteName = formula;
Sprite secondSprite;
try {
secondSprite = ProjectManager.getInstance().getSceneToPlay().getSpriteBySpriteName(secondSpriteName);
} catch (Resources.NotFoundException exception) {
return 0d;
}
Look firstLook = firstSprite.look;
Look secondLook;
if (secondSprite instanceof GroupSprite) {
List<Sprite> groupSprites = GroupSprite.getSpritesFromGroupWithGroupName(secondSpriteName);
for (Sprite sprite : groupSprites) {
secondLook = sprite.look;
if (CollisionDetection.checkCollisionBetweenLooks(firstLook, secondLook) == 1d) {
return 1d;
}
}
return 0d;
}
secondLook = secondSprite.look;
return CollisionDetection.checkCollisionBetweenLooks(firstLook, secondLook);
}
private Object interpretUserList(Sprite sprite) {
DataContainer dataContainer = ProjectManager.getInstance().getSceneToPlay().getDataContainer();
UserList userList = dataContainer.getUserList(value, sprite);
if (userList == null) {
return NOT_EXISTING_USER_LIST_INTERPRETATION_VALUE;
}
List<Object> userListValues = userList.getList();
if (userListValues.size() == 0) {
return 0d;
} else if (userListValues.size() == 1) {
Object userListValue = userListValues.get(0);
if (userListValue instanceof String) {
return userListValue;
} else {
return userListValue;
}
} else {
return interpretMultipleItemsUserList(userListValues);
}
}
private Object interpretMultipleItemsUserList(List<Object> userListValues) {
List<String> userListStringValues = new ArrayList<String>();
boolean concatenateWithoutWhitespace = true;
for (Object listValue : userListValues) {
if (listValue instanceof Double) {
Double doubleValueOfListItem = (Double) listValue;
if (isNumberAIntegerBetweenZeroAndNine(doubleValueOfListItem)) {
userListStringValues.add(doubleValueOfListItem.intValue() + "");
} else {
concatenateWithoutWhitespace = false;
userListStringValues.add(listValue.toString());
}
} else if (listValue instanceof String) {
String stringValueOfListItem = (String) listValue;
if (stringValueOfListItem.length() == 1) {
userListStringValues.add(stringValueOfListItem);
} else {
userListStringValues.add(stringValueOfListItem);
concatenateWithoutWhitespace = false;
}
}
}
String concatenatedList = "";
boolean isFirstListItem = true;
for (String userListStringValue : userListStringValues) {
if (!concatenateWithoutWhitespace && !isFirstListItem) {
concatenatedList += " ";
}
if (isFirstListItem) {
isFirstListItem = false;
}
concatenatedList += userListStringValue;
}
return concatenatedList;
}
private boolean isNumberAIntegerBetweenZeroAndNine(Double valueToCheck) {
for (Double index = 0.0; index <= 9.0; index++) {
if (valueToCheck.equals(index)) {
return true;
}
}
return false;
}
private Object interpretUserVariable(Sprite sprite) {
DataContainer userVariables = ProjectManager.getInstance().getSceneToPlay().getDataContainer();
UserVariable userVariable = userVariables.getUserVariable(value, sprite);
if (userVariable == null) {
return NOT_EXISTING_USER_VARIABLE_INTERPRETATION_VALUE;
}
Object userVariableValue = userVariable.getValue();
if (userVariableValue instanceof String) {
return userVariableValue;
} else {
return userVariableValue;
}
}
private Object interpretSensor(Sprite sprite) {
Sensors sensor = Sensors.getSensorByValue(value);
if (sensor.isObjectSensor) {
return interpretObjectSensor(sensor, sprite);
} else {
return SensorHandler.getSensorValue(sensor);
}
}
private Object interpretFunction(Functions function, Sprite sprite) {
Object left = null;
Object right = null;
Double doubleValueOfLeftChild = null;
Double doubleValueOfRightChild = null;
if (leftChild != null) {
left = leftChild.interpretRecursive(sprite);
if (left instanceof String) {
try {
doubleValueOfLeftChild = Double.valueOf((String) left);
} catch (NumberFormatException numberFormatException) {
Log.d(getClass().getSimpleName(), "Couldn't parse String", numberFormatException);
}
} else {
doubleValueOfLeftChild = (Double) left;
}
}
if (rightChild != null) {
right = rightChild.interpretRecursive(sprite);
if (right instanceof String) {
try {
doubleValueOfRightChild = Double.valueOf((String) right);
} catch (NumberFormatException numberFormatException) {
Log.d(getClass().getSimpleName(), "Couldn't parse String", numberFormatException);
}
} else {
doubleValueOfRightChild = (Double) right;
}
}
switch (function) {
case SIN:
return doubleValueOfLeftChild == null ? 0d : java.lang.Math.sin(Math.toRadians(doubleValueOfLeftChild));
case COS:
return doubleValueOfLeftChild == null ? 0d : java.lang.Math.cos(Math.toRadians(doubleValueOfLeftChild));
case TAN:
return doubleValueOfLeftChild == null ? 0d : java.lang.Math.tan(Math.toRadians(doubleValueOfLeftChild));
case LN:
return doubleValueOfLeftChild == null ? 0d : java.lang.Math.log(doubleValueOfLeftChild);
case LOG:
return doubleValueOfLeftChild == null ? 0d : java.lang.Math.log10(doubleValueOfLeftChild);
case SQRT:
return doubleValueOfLeftChild == null ? 0d : java.lang.Math.sqrt(doubleValueOfLeftChild);
case RAND:
return (doubleValueOfLeftChild == null || doubleValueOfRightChild == null) ? 0d : interpretFunctionRand(doubleValueOfLeftChild, doubleValueOfRightChild);
case ABS:
return doubleValueOfLeftChild == null ? 0d : java.lang.Math.abs(doubleValueOfLeftChild);
case ROUND:
return doubleValueOfLeftChild == null ? 0d : (double) java.lang.Math.round(doubleValueOfLeftChild);
case PI:
return java.lang.Math.PI;
case MOD:
return (doubleValueOfLeftChild == null || doubleValueOfRightChild == null) ? 0d : interpretFunctionMod(doubleValueOfLeftChild, doubleValueOfRightChild);
case ARCSIN:
return doubleValueOfLeftChild == null ? 0d : java.lang.Math.toDegrees(Math.asin(doubleValueOfLeftChild));
case ARCCOS:
return doubleValueOfLeftChild == null ? 0d : java.lang.Math.toDegrees(Math.acos(doubleValueOfLeftChild));
case ARCTAN:
return doubleValueOfLeftChild == null ? 0d : java.lang.Math.toDegrees(Math.atan(doubleValueOfLeftChild));
case EXP:
return doubleValueOfLeftChild == null ? 0d : java.lang.Math.exp(doubleValueOfLeftChild);
case POWER:
return (doubleValueOfLeftChild == null || doubleValueOfRightChild == null) ? 0d : java.lang.Math.pow(doubleValueOfLeftChild, doubleValueOfRightChild);
case FLOOR:
return doubleValueOfLeftChild == null ? 0d : java.lang.Math.floor(doubleValueOfLeftChild);
case CEIL:
return doubleValueOfLeftChild == null ? 0d : java.lang.Math.ceil(doubleValueOfLeftChild);
case MAX:
return (doubleValueOfLeftChild == null || doubleValueOfRightChild == null) ? 0d : java.lang.Math.max(doubleValueOfLeftChild,
doubleValueOfRightChild);
case MIN:
return (doubleValueOfLeftChild == null || doubleValueOfRightChild == null) ? 0d : java.lang.Math.min(doubleValueOfLeftChild,
doubleValueOfRightChild);
case TRUE:
return 1d;
case FALSE:
return 0d;
case LETTER:
return interpretFunctionLetter(right, left);
case LENGTH:
return interpretFunctionLength(left, sprite);
case JOIN:
return interpretFunctionJoin(sprite);
case ARDUINODIGITAL:
Arduino arduinoDigital = ServiceProvider.getService(CatroidService.BLUETOOTH_DEVICE_SERVICE).getDevice(BluetoothDevice.ARDUINO);
if (arduinoDigital != null && left != null) {
if (doubleValueOfLeftChild < 0 || doubleValueOfLeftChild > 13) {
return 0d;
}
return arduinoDigital.getDigitalArduinoPin(doubleValueOfLeftChild.intValue());
}
break;
case ARDUINOANALOG:
Arduino arduinoAnalog = ServiceProvider.getService(CatroidService.BLUETOOTH_DEVICE_SERVICE).getDevice(BluetoothDevice.ARDUINO);
if (arduinoAnalog != null && left != null) {
if (doubleValueOfLeftChild < 0 || doubleValueOfLeftChild > 5) {
return 0d;
}
return arduinoAnalog.getAnalogArduinoPin(doubleValueOfLeftChild.intValue());
}
break;
case RASPIDIGITAL:
RPiSocketConnection connection = RaspberryPiService.getInstance().connection;
int pin = doubleValueOfLeftChild.intValue();
try {
return connection.getPin(pin) ? 1d : 0d;
} catch (Exception e) {
Log.e(getClass().getSimpleName(), "RPi: exception during getPin: " + e);
}
break;
case MULTI_FINGER_TOUCHED:
return TouchUtil.isFingerTouching(doubleValueOfLeftChild.intValue()) ? 1d : 0d;
case MULTI_FINGER_X:
return Double.valueOf(TouchUtil.getX(doubleValueOfLeftChild.intValue()));
case MULTI_FINGER_Y:
return Double.valueOf(TouchUtil.getY(doubleValueOfLeftChild.intValue()));
case LIST_ITEM:
return interpretFunctionListItem(left, sprite);
case CONTAINS:
return interpretFunctionContains(right, sprite);
case NUMBER_OF_ITEMS:
return interpretFunctionNumberOfItems(left, sprite);
}
return 0d;
}
private Object interpretFunctionNumberOfItems(Object left, Sprite sprite) {
if (leftChild.type == ElementType.USER_LIST) {
return (double) handleNumberOfItemsOfUserListParameter(sprite);
}
return interpretFunctionLength(left, sprite);
}
private Object interpretFunctionContains(Object right, Sprite sprite) {
if (leftChild.getElementType() == ElementType.USER_LIST) {
DataContainer dataContainer = ProjectManager.getInstance().getSceneToPlay().getDataContainer();
UserList userList = dataContainer.getUserList(leftChild.getValue(), sprite);
if (userList == null) {
return 0d;
}
for (Object userListElement : userList.getList()) {
if (interpretOperatorEqual(userListElement, right) == 1d) {
return 1d;
}
}
}
return 0d;
}
private Object interpretFunctionListItem(Object left, Sprite sprite) {
UserList userList = null;
if (rightChild.getElementType() == ElementType.USER_LIST) {
DataContainer dataContainer = ProjectManager.getInstance().getSceneToPlay().getDataContainer();
userList = dataContainer.getUserList(rightChild.getValue(), sprite);
}
if (userList == null) {
return "";
}
int index = 0;
if (left instanceof String) {
try {
Double doubleValueOfLeftChild = Double.valueOf((String) left);
index = doubleValueOfLeftChild.intValue();
} catch (NumberFormatException numberFormatexception) {
Log.d(getClass().getSimpleName(), "Couldn't parse String", numberFormatexception);
}
} else {
index = ((Double) left).intValue();
}
index--;
if (index < 0) {
return "";
} else if (index >= userList.getList().size()) {
return "";
}
return userList.getList().get(index);
}
private Object interpretFunctionJoin(Sprite sprite) {
return interpretInterpretFunctionJoinParameter(leftChild, sprite)
+ interpretInterpretFunctionJoinParameter(rightChild, sprite);
}
private String interpretInterpretFunctionJoinParameter(FormulaElement child, Sprite sprite) {
String parameterInterpretation = "";
if (child != null) {
if (child.getElementType() == ElementType.NUMBER) {
Double number = Double.valueOf((String) child.interpretRecursive(sprite));
if (number.isNaN()) {
parameterInterpretation = "";
} else {
if (isInteger(number)) {
parameterInterpretation += number.intValue();
} else {
parameterInterpretation += number;
}
}
} else if (child.getElementType() == ElementType.STRING) {
parameterInterpretation = child.value;
} else {
parameterInterpretation += child.interpretRecursive(sprite);
}
}
return parameterInterpretation;
}
private Object interpretFunctionLength(Object left, Sprite sprite) {
if (leftChild == null) {
return 0d;
}
if (leftChild.type == ElementType.NUMBER) {
return (double) leftChild.value.length();
}
if (leftChild.type == ElementType.STRING) {
return (double) leftChild.value.length();
}
if (leftChild.type == ElementType.USER_VARIABLE) {
return (double) handleLengthUserVariableParameter(sprite);
}
if (leftChild.type == ElementType.USER_LIST) {
DataContainer dataContainer = ProjectManager.getInstance().getSceneToPlay().getDataContainer();
UserList userList = dataContainer.getUserList(leftChild.getValue(), sprite);
if (userList == null) {
return 0d;
}
if (userList.getList().size() == 0) {
return 0d;
}
Object interpretedList = leftChild.interpretRecursive(sprite);
if (interpretedList instanceof Double) {
Double interpretedListDoubleValue = (Double) interpretedList;
if (interpretedListDoubleValue.isNaN() || interpretedListDoubleValue.isInfinite()) {
return 0d;
}
return (double) (String.valueOf(interpretedListDoubleValue.intValue())).length();
}
if (interpretedList instanceof String) {
String interpretedListStringValue = (String) interpretedList;
return (double) interpretedListStringValue.length();
}
}
if (left instanceof Double && ((Double) left).isNaN()) {
return 0d;
}
return (double) (String.valueOf(left)).length();
}
private Object interpretFunctionLetter(Object right, Object left) {
int index = 0;
if (left instanceof String) {
try {
Double doubleValueOfLeftChild = Double.valueOf((String) left);
index = doubleValueOfLeftChild.intValue();
} catch (NumberFormatException numberFormatexception) {
Log.d(getClass().getSimpleName(), "Couldn't parse String", numberFormatexception);
}
} else {
index = ((Double) left).intValue();
}
index--;
if (index < 0) {
return "";
} else if (index >= String.valueOf(right).length()) {
return "";
}
return String.valueOf(String.valueOf(right).charAt(index));
}
private Object interpretFunctionMod(Object left, Object right) {
double dividend = (Double) left;
double divisor = (Double) right;
if (dividend == 0 || divisor == 0) {
return dividend;
}
if (divisor > 0) {
while (dividend < 0) {
dividend += java.lang.Math.abs(divisor);
}
} else {
if (dividend > 0) {
return (dividend % divisor) + divisor;
}
}
return dividend % divisor;
}
private Object interpretFunctionRand(Object right, Object left) {
Double minimum = java.lang.Math.min((Double) left, (Double) right);
Double maximum = java.lang.Math.max((Double) left, (Double) right);
Double randomDouble = minimum + (java.lang.Math.random() * (maximum - minimum));
if (isInteger(minimum) && isInteger(maximum)
&& !(rightChild.type == ElementType.NUMBER && rightChild.value.contains("."))
&& !(leftChild.type == ElementType.NUMBER && leftChild.value.contains("."))) {
if ((Math.abs(randomDouble) - (int) Math.abs(randomDouble)) >= 0.5) {
return (double) randomDouble.intValue() + 1;
} else {
return (double) randomDouble.intValue();
}
} else {
return randomDouble;
}
}
private Object interpretOperator(Operators operator, Sprite sprite) {
if (leftChild != null) { // binary operator
Object leftObject;
Object rightObject;
try {
leftObject = leftChild.interpretRecursive(sprite);
} catch (NumberFormatException numberFormatException) {
leftObject = Double.NaN;
}
try {
rightObject = rightChild.interpretRecursive(sprite);
} catch (NumberFormatException numberFormatException) {
rightObject = Double.NaN;
}
Double left;
Double right;
switch (operator) {
case PLUS:
left = interpretOperator(leftObject);
right = interpretOperator(rightObject);
return left + right;
case MINUS:
left = interpretOperator(leftObject);
right = interpretOperator(rightObject);
return left - right;
case MULT:
left = interpretOperator(leftObject);
right = interpretOperator(rightObject);
return left * right;
case DIVIDE:
left = interpretOperator(leftObject);
right = interpretOperator(rightObject);
return left / right;
case POW:
left = interpretOperator(leftObject);
right = interpretOperator(rightObject);
return java.lang.Math.pow(left, right);
case EQUAL:
return interpretOperatorEqual(leftObject, rightObject);
case NOT_EQUAL:
return interpretOperatorEqual(leftObject, rightObject) == 1d ? 0d : 1d;
case GREATER_THAN:
left = interpretOperator(leftObject);
right = interpretOperator(rightObject);
return left.compareTo(right) > 0 ? 1d : 0d;
case GREATER_OR_EQUAL:
left = interpretOperator(leftObject);
right = interpretOperator(rightObject);
return left.compareTo(right) >= 0 ? 1d : 0d;
case SMALLER_THAN:
left = interpretOperator(leftObject);
right = interpretOperator(rightObject);
return left.compareTo(right) < 0 ? 1d : 0d;
case SMALLER_OR_EQUAL:
left = interpretOperator(leftObject);
right = interpretOperator(rightObject);
return left.compareTo(right) <= 0 ? 1d : 0d;
case LOGICAL_AND:
left = interpretOperator(leftObject);
right = interpretOperator(rightObject);
return (left * right) != 0d ? 1d : 0d;
case LOGICAL_OR:
left = interpretOperator(leftObject);
right = interpretOperator(rightObject);
return left != 0d || right != 0d ? 1d : 0d;
}
} else { //unary operators
Object rightObject;
try {
rightObject = rightChild.interpretRecursive(sprite);
} catch (NumberFormatException numberFormatException) {
rightObject = Double.NaN;
}
switch (operator) {
case MINUS:
Double result = interpretOperator(rightObject);
return -result;
case LOGICAL_NOT:
return interpretOperator(rightObject) == 0d ? 1d : 0d;
}
}
return 0d;
}
private Object interpretObjectSensor(Sensors sensor, Sprite sprite) {
Object returnValue = 0d;
LookData lookData = sprite.look.getLookData();
List<LookData> lookDataList = sprite.getLookDataList();
if (lookData == null && lookDataList.size() > 0) {
lookData = lookDataList.get(0);
}
switch (sensor) {
case OBJECT_BRIGHTNESS:
returnValue = (double) sprite.look.getBrightnessInUserInterfaceDimensionUnit();
break;
case OBJECT_COLOR:
returnValue = (double) sprite.look.getColorInUserInterfaceDimensionUnit();
break;
case OBJECT_TRANSPARENCY:
returnValue = (double) sprite.look.getTransparencyInUserInterfaceDimensionUnit();
break;
case OBJECT_LAYER:
returnValue = (double) sprite.look.getZIndex() - Constants.Z_INDEX_NUMBER_VIRTUAL_LAYERS;
break;
case OBJECT_ROTATION:
returnValue = (double) sprite.look.getDirectionInUserInterfaceDimensionUnit();
break;
case OBJECT_SIZE:
returnValue = (double) sprite.look.getSizeInUserInterfaceDimensionUnit();
break;
case OBJECT_X:
returnValue = (double) sprite.look.getXInUserInterfaceDimensionUnit();
break;
case OBJECT_Y:
returnValue = (double) sprite.look.getYInUserInterfaceDimensionUnit();
break;
case OBJECT_ANGULAR_VELOCITY:
returnValue = (double) sprite.look.getAngularVelocityInUserInterfaceDimensionUnit();
break;
case OBJECT_X_VELOCITY:
returnValue = (double) sprite.look.getXVelocityInUserInterfaceDimensionUnit();
break;
case OBJECT_Y_VELOCITY:
returnValue = (double) sprite.look.getYVelocityInUserInterfaceDimensionUnit();
break;
case OBJECT_LOOK_NUMBER:
case OBJECT_BACKGROUND_NUMBER:
returnValue = 1.0d + ((lookData != null) ? lookDataList.indexOf(lookData) : 0);
break;
case OBJECT_LOOK_NAME:
case OBJECT_BACKGROUND_NAME:
returnValue = (lookData != null) ? lookData.getLookName() : "";
break;
case OBJECT_DISTANCE_TO:
returnValue = (double) sprite.look.getDistanceToTouchPositionInUserInterfaceDimensions();
break;
case NFC_TAG_MESSAGE:
returnValue = NfcHandler.getLastNfcTagMessage();
break;
case NFC_TAG_ID:
returnValue = NfcHandler.getLastNfcTagId();
break;
case COLLIDES_WITH_EDGE:
//if the stage is not setUp yet, there can't be a collision
returnValue = StageActivity.stageListener.firstFrameDrawn ? CollisionDetection.collidesWithEdge(sprite
.look) : 0d;
break;
case COLLIDES_WITH_FINGER:
returnValue = CollisionDetection.collidesWithFinger(sprite.look);
break;
}
return returnValue;
}
private Double interpretOperatorEqual(Object left, Object right) {
if (left instanceof String && right instanceof String) {
try {
return (Double.valueOf((String) left).compareTo(Double.valueOf((String) right))) == 0 ? 1d : 0;
} catch (NumberFormatException numberFormatException) {
int compareResult = ((String) left).compareTo((String) right);
if (compareResult == 0) {
return 1d;
}
}
}
if (left instanceof Double && right instanceof String) {
try {
int compareResult = ((Double) left).compareTo(Double.valueOf((String) right));
if (compareResult == 0) {
return 1d;
}
} catch (NumberFormatException numberFormatException) {
return 0d;
}
}
if (left instanceof String && right instanceof Double) {
try {
int compareResult = Double.valueOf((String) left).compareTo((Double) right);
if (compareResult == 0) {
return 1d;
}
} catch (NumberFormatException numberFormatException) {
return 0d;
}
}
if (left instanceof Double && right instanceof Double) {
return (((Double) left).compareTo((Double) right) == 0) ? 1d : 0d;
}
return 0d;
}
private Double interpretOperator(Object object) {
if (object instanceof String) {
try {
return Double.valueOf((String) object);
} catch (NumberFormatException numberFormatException) {
return Double.NaN;
}
} else {
return (Double) object;
}
}
private Object normalizeDegeneratedDoubleValues(Object valueToCheck) {
if (valueToCheck instanceof String || valueToCheck instanceof Character) {
return valueToCheck;
}
if (valueToCheck == null) {
return 0.0;
}
if ((Double) valueToCheck == Double.NEGATIVE_INFINITY) {
return -Double.MAX_VALUE;
}
if ((Double) valueToCheck == Double.POSITIVE_INFINITY) {
return Double.MAX_VALUE;
}
return valueToCheck;
}
public FormulaElement getParent() {
return parent;
}
public void setRightChild(FormulaElement rightChild) {
this.rightChild = rightChild;
this.rightChild.parent = this;
}
public void setLeftChild(FormulaElement leftChild) {
this.leftChild = leftChild;
this.leftChild.parent = this;
}
public void replaceElement(FormulaElement current) {
parent = current.parent;
leftChild = current.leftChild;
rightChild = current.rightChild;
value = current.value;
type = current.type;
if (leftChild != null) {
leftChild.parent = this;
}
if (rightChild != null) {
rightChild.parent = this;
}
}
public void replaceElement(ElementType type, String value) {
this.value = value;
this.type = type;
}
public void replaceWithSubElement(String operator, FormulaElement rightChild) {
FormulaElement cloneThis = new FormulaElement(ElementType.OPERATOR, operator, this.getParent(), this,
rightChild);
cloneThis.parent.rightChild = cloneThis;
}
private boolean isInteger(double value) {
return ((Math.abs(value) - (int) Math.abs(value)) < Double.MIN_VALUE);
}
public boolean isLogicalOperator() {
return (type == ElementType.OPERATOR) && Operators.getOperatorByValue(value).isLogicalOperator;
}
public boolean containsElement(ElementType elementType) {
if (type.equals(elementType)
|| (leftChild != null && leftChild.containsElement(elementType))
|| (rightChild != null && rightChild.containsElement(elementType))) {
return true;
}
return false;
}
public boolean isUserVariableWithTypeString(Sprite sprite) {
if (type == ElementType.USER_VARIABLE) {
DataContainer userVariableContainer = ProjectManager.getInstance().getSceneToPlay()
.getDataContainer();
UserVariable userVariable = userVariableContainer.getUserVariable(value, sprite);
Object userVariableValue = userVariable.getValue();
return userVariableValue instanceof String;
}
return false;
}
private int handleLengthUserVariableParameter(Sprite sprite) {
DataContainer userVariableContainer = ProjectManager.getInstance().getSceneToPlay()
.getDataContainer();
UserVariable userVariable = userVariableContainer.getUserVariable(leftChild.value, sprite);
Object userVariableValue = userVariable.getValue();
if (userVariableValue instanceof String) {
return String.valueOf(userVariableValue).length();
} else {
if (isInteger((Double) userVariableValue)) {
return Integer.toString(((Double) userVariableValue).intValue()).length();
} else {
return Double.toString(((Double) userVariableValue)).length();
}
}
}
private int handleNumberOfItemsOfUserListParameter(Sprite sprite) {
DataContainer dataContainer = ProjectManager.getInstance().getSceneToPlay()
.getDataContainer();
UserList userList = dataContainer.getUserList(leftChild.value, sprite);
if (userList == null) {
return 0;
}
return userList.getList().size();
}
public boolean isSingleNumberFormula() {
if (type == ElementType.OPERATOR) {
Operators operator = Operators.getOperatorByValue(value);
return (operator == Operators.MINUS) && (leftChild == null) && rightChild.isSingleNumberFormula();
} else if (type == ElementType.NUMBER) {
return true;
}
return false;
}
@Override
public FormulaElement clone() {
FormulaElement leftChildClone = leftChild == null ? null : leftChild.clone();
FormulaElement rightChildClone = rightChild == null ? null : rightChild.clone();
return new FormulaElement(type, value == null ? "" : value, null, leftChildClone, rightChildClone);
}
public int getRequiredResources() {
int resources = Brick.NO_RESOURCES;
if (leftChild != null) {
resources |= leftChild.getRequiredResources();
}
if (rightChild != null) {
resources |= rightChild.getRequiredResources();
}
if (type == ElementType.FUNCTION) {
Functions functions = Functions.getFunctionByValue(value);
switch (functions) {
case ARDUINOANALOG:
case ARDUINODIGITAL:
resources |= Brick.BLUETOOTH_SENSORS_ARDUINO;
break;
case RASPIDIGITAL:
resources |= Brick.SOCKET_RASPI;
break;
}
}
if (type == ElementType.SENSOR) {
Sensors sensor = Sensors.getSensorByValue(value);
switch (sensor) {
case X_ACCELERATION:
case Y_ACCELERATION:
case Z_ACCELERATION:
resources |= Brick.SENSOR_ACCELERATION;
break;
case X_INCLINATION:
case Y_INCLINATION:
resources |= Brick.SENSOR_INCLINATION;
break;
case COMPASS_DIRECTION:
resources |= Brick.SENSOR_COMPASS;
break;
case LATITUDE:
case LONGITUDE:
case LOCATION_ACCURACY:
case ALTITUDE:
resources |= Brick.SENSOR_GPS;
break;
case FACE_DETECTED:
case FACE_SIZE:
case FACE_X_POSITION:
case FACE_Y_POSITION:
resources |= Brick.FACE_DETECTION;
break;
case NXT_SENSOR_1:
case NXT_SENSOR_2:
case NXT_SENSOR_3:
case NXT_SENSOR_4:
resources |= Brick.BLUETOOTH_LEGO_NXT;
break;
case EV3_SENSOR_1:
case EV3_SENSOR_2:
case EV3_SENSOR_3:
case EV3_SENSOR_4:
resources |= Brick.BLUETOOTH_LEGO_EV3;
break;
case PHIRO_FRONT_LEFT:
case PHIRO_FRONT_RIGHT:
case PHIRO_SIDE_LEFT:
case PHIRO_SIDE_RIGHT:
case PHIRO_BOTTOM_LEFT:
case PHIRO_BOTTOM_RIGHT:
resources |= Brick.BLUETOOTH_PHIRO;
break;
case DRONE_BATTERY_STATUS:
case DRONE_CAMERA_READY:
case DRONE_EMERGENCY_STATE:
case DRONE_FLYING:
case DRONE_INITIALIZED:
case DRONE_NUM_FRAMES:
case DRONE_RECORD_READY:
case DRONE_RECORDING:
case DRONE_USB_ACTIVE:
case DRONE_USB_REMAINING_TIME:
resources |= Brick.ARDRONE_SUPPORT;
break;
case NFC_TAG_MESSAGE:
case NFC_TAG_ID:
resources |= Brick.NFC_ADAPTER;
break;
case COLLIDES_WITH_EDGE:
resources |= Brick.COLLISION;
break;
case COLLIDES_WITH_FINGER:
resources |= Brick.COLLISION;
break;
default:
}
}
if (type == ElementType.COLLISION_FORMULA) {
resources |= Brick.COLLISION;
}
return resources;
}
}