Related
I'm trying to write a Java routine to evaluate math expressions from String values like:
"5+3"
"10-4*5"
"(1+10)*3"
I want to avoid a lot of if-then-else statements.
How can I do this?
With JDK1.6, you can use the built-in Javascript engine.
import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
public class Test {
public static void main(String[] args) throws ScriptException {
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript");
String foo = "40+2";
System.out.println(engine.eval(foo));
}
}
I've written this eval method for arithmetic expressions to answer this question. It does addition, subtraction, multiplication, division, exponentiation (using the ^ symbol), and a few basic functions like sqrt. It supports grouping using (...), and it gets the operator precedence and associativity rules correct.
public static double eval(final String str) {
return new Object() {
int pos = -1, ch;
void nextChar() {
ch = (++pos < str.length()) ? str.charAt(pos) : -1;
}
boolean eat(int charToEat) {
while (ch == ' ') nextChar();
if (ch == charToEat) {
nextChar();
return true;
}
return false;
}
double parse() {
nextChar();
double x = parseExpression();
if (pos < str.length()) throw new RuntimeException("Unexpected: " + (char)ch);
return x;
}
// Grammar:
// expression = term | expression `+` term | expression `-` term
// term = factor | term `*` factor | term `/` factor
// factor = `+` factor | `-` factor | `(` expression `)` | number
// | functionName `(` expression `)` | functionName factor
// | factor `^` factor
double parseExpression() {
double x = parseTerm();
for (;;) {
if (eat('+')) x += parseTerm(); // addition
else if (eat('-')) x -= parseTerm(); // subtraction
else return x;
}
}
double parseTerm() {
double x = parseFactor();
for (;;) {
if (eat('*')) x *= parseFactor(); // multiplication
else if (eat('/')) x /= parseFactor(); // division
else return x;
}
}
double parseFactor() {
if (eat('+')) return +parseFactor(); // unary plus
if (eat('-')) return -parseFactor(); // unary minus
double x;
int startPos = this.pos;
if (eat('(')) { // parentheses
x = parseExpression();
if (!eat(')')) throw new RuntimeException("Missing ')'");
} else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers
while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
x = Double.parseDouble(str.substring(startPos, this.pos));
} else if (ch >= 'a' && ch <= 'z') { // functions
while (ch >= 'a' && ch <= 'z') nextChar();
String func = str.substring(startPos, this.pos);
if (eat('(')) {
x = parseExpression();
if (!eat(')')) throw new RuntimeException("Missing ')' after argument to " + func);
} else {
x = parseFactor();
}
if (func.equals("sqrt")) x = Math.sqrt(x);
else if (func.equals("sin")) x = Math.sin(Math.toRadians(x));
else if (func.equals("cos")) x = Math.cos(Math.toRadians(x));
else if (func.equals("tan")) x = Math.tan(Math.toRadians(x));
else throw new RuntimeException("Unknown function: " + func);
} else {
throw new RuntimeException("Unexpected: " + (char)ch);
}
if (eat('^')) x = Math.pow(x, parseFactor()); // exponentiation
return x;
}
}.parse();
}
Example:
System.out.println(eval("((4 - 2^3 + 1) * -sqrt(3*3+4*4)) / 2"));
Output: 7.5 (which is correct)
The parser is a recursive descent parser, so internally uses separate parse methods for each level of operator precedence in its grammar. I deliberately kept it short, but here are some ideas you might want to expand it with:
Variables:
The bit of the parser that reads the names for functions can easily be changed to handle custom variables too, by looking up names in a variable table passed to the eval method, such as a Map<String,Double> variables.
Separate compilation and evaluation:
What if, having added support for variables, you wanted to evaluate the same expression millions of times with changed variables, without parsing it every time? It's possible. First define an interface to use to evaluate the precompiled expression:
#FunctionalInterface
interface Expression {
double eval();
}
Now to rework the original "eval" function into a "parse" function, change all the methods that return doubles, so instead they return an instance of that interface. Java 8's lambda syntax works well for this. Example of one of the changed methods:
Expression parseExpression() {
Expression x = parseTerm();
for (;;) {
if (eat('+')) { // addition
Expression a = x, b = parseTerm();
x = (() -> a.eval() + b.eval());
} else if (eat('-')) { // subtraction
Expression a = x, b = parseTerm();
x = (() -> a.eval() - b.eval());
} else {
return x;
}
}
}
That builds a recursive tree of Expression objects representing the compiled expression (an abstract syntax tree). Then you can compile it once and evaluate it repeatedly with different values:
public static void main(String[] args) {
Map<String,Double> variables = new HashMap<>();
Expression exp = parse("x^2 - x + 2", variables);
for (double x = -20; x <= +20; x++) {
variables.put("x", x);
System.out.println(x + " => " + exp.eval());
}
}
Different datatypes:
Instead of double, you could change the evaluator to use something more powerful like BigDecimal, or a class that implements complex numbers, or rational numbers (fractions). You could even use Object, allowing some mix of datatypes in expressions, just like a real programming language. :)
All code in this answer released to the public domain. Have fun!
For my university project, I was looking for a parser / evaluator supporting both basic formulas and more complicated equations (especially iterated operators). I found very nice open source library for JAVA and .NET called mXparser. I will give a few examples to make some feeling on the syntax, for further instructions please visit project website (especially tutorial section).
https://mathparser.org/
https://mathparser.org/mxparser-tutorial/
https://mathparser.org/api/
And few examples
1 - Simple furmula
Expression e = new Expression("( 2 + 3/4 + sin(pi) )/2");
double v = e.calculate()
2 - User defined arguments and constants
Argument x = new Argument("x = 10");
Constant a = new Constant("a = pi^2");
Expression e = new Expression("cos(a*x)", x, a);
double v = e.calculate()
3 - User defined functions
Function f = new Function("f(x, y, z) = sin(x) + cos(y*z)");
Expression e = new Expression("f(3,2,5)", f);
double v = e.calculate()
4 - Iteration
Expression e = new Expression("sum( i, 1, 100, sin(i) )");
double v = e.calculate()
Found recently - in case you would like to try the syntax (and see the advanced use case) you can download the Scalar Calculator app that is powered by mXparser.
The correct way to solve this is with a lexer and a parser. You can write simple versions of these yourself, or those pages also have links to Java lexers and parsers.
Creating a recursive descent parser is a really good learning exercise.
HERE is another open source library on GitHub named EvalEx.
Unlike the JavaScript engine this library is focused in evaluating mathematical expressions only. Moreover, the library is extensible and supports use of boolean operators as well as parentheses.
You can also try the BeanShell interpreter:
Interpreter interpreter = new Interpreter();
interpreter.eval("result = (7+21*6)/(32-27)");
System.out.println(interpreter.get("result"));
You can evaluate expressions easily if your Java application already accesses a database, without using any other JARs.
Some databases require you to use a dummy table (eg, Oracle's "dual" table) and others will allow you to evaluate expressions without "selecting" from any table.
For example, in Sql Server or Sqlite
select (((12.10 +12.0))/ 233.0) amount
and in Oracle
select (((12.10 +12.0))/ 233.0) amount from dual;
The advantage of using a DB is that you can evaluate many expressions at the same time. Also most DB's will allow you to use highly complex expressions and will also have a number of extra functions that can be called as necessary.
However performance may suffer if many single expressions need to be evaluated individually, particularly when the DB is located on a network server.
The following addresses the performance problem to some extent, by using a Sqlite in-memory database.
Here's a full working example in Java
Class. forName("org.sqlite.JDBC");
Connection conn = DriverManager.getConnection("jdbc:sqlite::memory:");
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery( "select (1+10)/20.0 amount");
rs.next();
System.out.println(rs.getBigDecimal(1));
stat.close();
conn.close();
Of course you could extend the above code to handle multiple calculations at the same time.
ResultSet rs = stat.executeQuery( "select (1+10)/20.0 amount, (1+100)/20.0 amount2");
Another way is to use the Spring Expression Language or SpEL which does a whole lot more along with evaluating mathematical expressions, therefore maybe slightly overkill. You do not have to be using Spring framework to use this expression library as it is stand-alone. Copying examples from SpEL's documentation:
ExpressionParser parser = new SpelExpressionParser();
int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2
double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); //24.0
This article discusses various approaches. Here are the 2 key approaches mentioned in the article:
JEXL from Apache
Allows for scripts that include references to java objects.
// Create or retrieve a JexlEngine
JexlEngine jexl = new JexlEngine();
// Create an expression object
String jexlExp = "foo.innerFoo.bar()";
Expression e = jexl.createExpression( jexlExp );
// Create a context and add data
JexlContext jctx = new MapContext();
jctx.set("foo", new Foo() );
// Now evaluate the expression, getting the result
Object o = e.evaluate(jctx);
Use the javascript engine embedded in the JDK:
private static void jsEvalWithVariable()
{
List<String> namesList = new ArrayList<String>();
namesList.add("Jill");
namesList.add("Bob");
namesList.add("Laureen");
namesList.add("Ed");
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");
jsEngine.put("namesListKey", namesList);
System.out.println("Executing in script environment...");
try
{
jsEngine.eval("var x;" +
"var names = namesListKey.toArray();" +
"for(x in names) {" +
" println(names[x]);" +
"}" +
"namesListKey.add(\"Dana\");");
}
catch (ScriptException ex)
{
ex.printStackTrace();
}
}
if we are going to implement it then we can can use the below algorithm :--
While there are still tokens to be read in,
1.1 Get the next token.
1.2 If the token is:
1.2.1 A number: push it onto the value stack.
1.2.2 A variable: get its value, and push onto the value stack.
1.2.3 A left parenthesis: push it onto the operator stack.
1.2.4 A right parenthesis:
1 While the thing on top of the operator stack is not a
left parenthesis,
1 Pop the operator from the operator stack.
2 Pop the value stack twice, getting two operands.
3 Apply the operator to the operands, in the correct order.
4 Push the result onto the value stack.
2 Pop the left parenthesis from the operator stack, and discard it.
1.2.5 An operator (call it thisOp):
1 While the operator stack is not empty, and the top thing on the
operator stack has the same or greater precedence as thisOp,
1 Pop the operator from the operator stack.
2 Pop the value stack twice, getting two operands.
3 Apply the operator to the operands, in the correct order.
4 Push the result onto the value stack.
2 Push thisOp onto the operator stack.
While the operator stack is not empty,
1 Pop the operator from the operator stack.
2 Pop the value stack twice, getting two operands.
3 Apply the operator to the operands, in the correct order.
4 Push the result onto the value stack.
At this point the operator stack should be empty, and the value
stack should have only one value in it, which is the final result.
This is another interesting alternative
https://github.com/Shy-Ta/expression-evaluator-demo
The usage is very simple and gets the job done, for example:
ExpressionsEvaluator evalExpr = ExpressionsFactory.create("2+3*4-6/2");
assertEquals(BigDecimal.valueOf(11), evalExpr.eval());
It seems like JEP should do the job
It's too late to answer but I came across same situation to evaluate expression in java, it might help someone
MVEL does runtime evaluation of expressions, we can write a java code in String to get it evaluated in this.
String expressionStr = "x+y";
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("x", 10);
vars.put("y", 20);
ExecutableStatement statement = (ExecutableStatement) MVEL.compileExpression(expressionStr);
Object result = MVEL.executeExpression(statement, vars);
Try the following sample code using JDK1.6's Javascript engine with code injection handling.
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
public class EvalUtil {
private static ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
public static void main(String[] args) {
try {
System.out.println((new EvalUtil()).eval("(((5+5)/2) > 5) || 5 >3 "));
System.out.println((new EvalUtil()).eval("(((5+5)/2) > 5) || true"));
} catch (Exception e) {
e.printStackTrace();
}
}
public Object eval(String input) throws Exception{
try {
if(input.matches(".*[a-zA-Z;~`#$_{}\\[\\]:\\\\;\"',\\.\\?]+.*")) {
throw new Exception("Invalid expression : " + input );
}
return engine.eval(input);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
}
This is actually complementing the answer given by #Boann. It has a slight bug which causes "-2 ^ 2" to give an erroneous result of -4.0. The problem for that is the point at which the exponentiation is evaluated in his. Just move the exponentiation to the block of parseTerm(), and you'll be all fine. Have a look at the below, which is #Boann's answer slightly modified. Modification is in the comments.
public static double eval(final String str) {
return new Object() {
int pos = -1, ch;
void nextChar() {
ch = (++pos < str.length()) ? str.charAt(pos) : -1;
}
boolean eat(int charToEat) {
while (ch == ' ') nextChar();
if (ch == charToEat) {
nextChar();
return true;
}
return false;
}
double parse() {
nextChar();
double x = parseExpression();
if (pos < str.length()) throw new RuntimeException("Unexpected: " + (char)ch);
return x;
}
// Grammar:
// expression = term | expression `+` term | expression `-` term
// term = factor | term `*` factor | term `/` factor
// factor = `+` factor | `-` factor | `(` expression `)`
// | number | functionName factor | factor `^` factor
double parseExpression() {
double x = parseTerm();
for (;;) {
if (eat('+')) x += parseTerm(); // addition
else if (eat('-')) x -= parseTerm(); // subtraction
else return x;
}
}
double parseTerm() {
double x = parseFactor();
for (;;) {
if (eat('*')) x *= parseFactor(); // multiplication
else if (eat('/')) x /= parseFactor(); // division
else if (eat('^')) x = Math.pow(x, parseFactor()); //exponentiation -> Moved in to here. So the problem is fixed
else return x;
}
}
double parseFactor() {
if (eat('+')) return parseFactor(); // unary plus
if (eat('-')) return -parseFactor(); // unary minus
double x;
int startPos = this.pos;
if (eat('(')) { // parentheses
x = parseExpression();
eat(')');
} else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers
while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
x = Double.parseDouble(str.substring(startPos, this.pos));
} else if (ch >= 'a' && ch <= 'z') { // functions
while (ch >= 'a' && ch <= 'z') nextChar();
String func = str.substring(startPos, this.pos);
x = parseFactor();
if (func.equals("sqrt")) x = Math.sqrt(x);
else if (func.equals("sin")) x = Math.sin(Math.toRadians(x));
else if (func.equals("cos")) x = Math.cos(Math.toRadians(x));
else if (func.equals("tan")) x = Math.tan(Math.toRadians(x));
else throw new RuntimeException("Unknown function: " + func);
} else {
throw new RuntimeException("Unexpected: " + (char)ch);
}
//if (eat('^')) x = Math.pow(x, parseFactor()); // exponentiation -> This is causing a bit of problem
return x;
}
}.parse();
}
import java.util.*;
public class check {
int ans;
String str="7 + 5";
StringTokenizer st=new StringTokenizer(str);
int v1=Integer.parseInt(st.nextToken());
String op=st.nextToken();
int v2=Integer.parseInt(st.nextToken());
if(op.equals("+")) { ans= v1 + v2; }
if(op.equals("-")) { ans= v1 - v2; }
//.........
}
I think what ever way you do this it's going to involve a lot of conditional statements. But for single operations like in your examples you could limit it to 4 if statements with something like
String math = "1+4";
if (math.split("+").length == 2) {
//do calculation
} else if (math.split("-").length == 2) {
//do calculation
} ...
It gets a whole lot more complicated when you want to deal with multiple operations like "4+5*6".
If you are trying to build a calculator then I'd surgest passing each section of the calculation separatly (each number or operator) rather than as a single string.
You might have a look at the Symja framework:
ExprEvaluator util = new ExprEvaluator();
IExpr result = util.evaluate("10-40");
System.out.println(result.toString()); // -> "-30"
Take note that definitively more complex expressions can be evaluated:
// D(...) gives the derivative of the function Sin(x)*Cos(x)
IAST function = D(Times(Sin(x), Cos(x)), x);
IExpr result = util.evaluate(function);
// print: Cos(x)^2-Sin(x)^2
package ExpressionCalculator.expressioncalculator;
import java.text.DecimalFormat;
import java.util.Scanner;
public class ExpressionCalculator {
private static String addSpaces(String exp){
//Add space padding to operands.
//https://regex101.com/r/sJ9gM7/73
exp = exp.replaceAll("(?<=[0-9()])[\\/]", " / ");
exp = exp.replaceAll("(?<=[0-9()])[\\^]", " ^ ");
exp = exp.replaceAll("(?<=[0-9()])[\\*]", " * ");
exp = exp.replaceAll("(?<=[0-9()])[+]", " + ");
exp = exp.replaceAll("(?<=[0-9()])[-]", " - ");
//Keep replacing double spaces with single spaces until your string is properly formatted
/*while(exp.indexOf(" ") != -1){
exp = exp.replace(" ", " ");
}*/
exp = exp.replaceAll(" {2,}", " ");
return exp;
}
public static Double evaluate(String expr){
DecimalFormat df = new DecimalFormat("#.####");
//Format the expression properly before performing operations
String expression = addSpaces(expr);
try {
//We will evaluate using rule BDMAS, i.e. brackets, division, power, multiplication, addition and
//subtraction will be processed in following order
int indexClose = expression.indexOf(")");
int indexOpen = -1;
if (indexClose != -1) {
String substring = expression.substring(0, indexClose);
indexOpen = substring.lastIndexOf("(");
substring = substring.substring(indexOpen + 1).trim();
if(indexOpen != -1 && indexClose != -1) {
Double result = evaluate(substring);
expression = expression.substring(0, indexOpen).trim() + " " + result + " " + expression.substring(indexClose + 1).trim();
return evaluate(expression.trim());
}
}
String operation = "";
if(expression.indexOf(" / ") != -1){
operation = "/";
}else if(expression.indexOf(" ^ ") != -1){
operation = "^";
} else if(expression.indexOf(" * ") != -1){
operation = "*";
} else if(expression.indexOf(" + ") != -1){
operation = "+";
} else if(expression.indexOf(" - ") != -1){ //Avoid negative numbers
operation = "-";
} else{
return Double.parseDouble(expression);
}
int index = expression.indexOf(operation);
if(index != -1){
indexOpen = expression.lastIndexOf(" ", index - 2);
indexOpen = (indexOpen == -1)?0:indexOpen;
indexClose = expression.indexOf(" ", index + 2);
indexClose = (indexClose == -1)?expression.length():indexClose;
if(indexOpen != -1 && indexClose != -1) {
Double lhs = Double.parseDouble(expression.substring(indexOpen, index));
Double rhs = Double.parseDouble(expression.substring(index + 2, indexClose));
Double result = null;
switch (operation){
case "/":
//Prevent divide by 0 exception.
if(rhs == 0){
return null;
}
result = lhs / rhs;
break;
case "^":
result = Math.pow(lhs, rhs);
break;
case "*":
result = lhs * rhs;
break;
case "-":
result = lhs - rhs;
break;
case "+":
result = lhs + rhs;
break;
default:
break;
}
if(indexClose == expression.length()){
expression = expression.substring(0, indexOpen) + " " + result + " " + expression.substring(indexClose);
}else{
expression = expression.substring(0, indexOpen) + " " + result + " " + expression.substring(indexClose + 1);
}
return Double.valueOf(df.format(evaluate(expression.trim())));
}
}
}catch(Exception exp){
exp.printStackTrace();
}
return 0.0;
}
public static void main(String args[]){
Scanner scanner = new Scanner(System.in);
System.out.print("Enter an Mathematical Expression to Evaluate: ");
String input = scanner.nextLine();
System.out.println(evaluate(input));
}
}
A Java class that can evaluate mathematical expressions:
package test;
public class Calculator {
public static Double calculate(String expression){
if (expression == null || expression.length() == 0) {
return null;
}
return calc(expression.replace(" ", ""));
}
public static Double calc(String expression) {
String[] containerArr = new String[]{expression};
double leftVal = getNextOperand(containerArr);
expression = containerArr[0];
if (expression.length() == 0) {
return leftVal;
}
char operator = expression.charAt(0);
expression = expression.substring(1);
while (operator == '*' || operator == '/') {
containerArr[0] = expression;
double rightVal = getNextOperand(containerArr);
expression = containerArr[0];
if (operator == '*') {
leftVal = leftVal * rightVal;
} else {
leftVal = leftVal / rightVal;
}
if (expression.length() > 0) {
operator = expression.charAt(0);
expression = expression.substring(1);
} else {
return leftVal;
}
}
if (operator == '+') {
return leftVal + calc(expression);
} else {
return leftVal - calc(expression);
}
}
private static double getNextOperand(String[] exp){
double res;
if (exp[0].startsWith("(")) {
int open = 1;
int i = 1;
while (open != 0) {
if (exp[0].charAt(i) == '(') {
open++;
} else if (exp[0].charAt(i) == ')') {
open--;
}
i++;
}
res = calc(exp[0].substring(1, i - 1));
exp[0] = exp[0].substring(i);
} else {
int i = 1;
if (exp[0].charAt(0) == '-') {
i++;
}
while (exp[0].length() > i && isNumber((int) exp[0].charAt(i))) {
i++;
}
res = Double.parseDouble(exp[0].substring(0, i));
exp[0] = exp[0].substring(i);
}
return res;
}
private static boolean isNumber(int c) {
int zero = (int) '0';
int nine = (int) '9';
return (c >= zero && c <= nine) || c =='.';
}
public static void main(String[] args) {
System.out.println(calculate("(((( -6 )))) * 9 * -1"));
System.out.println(calc("(-5.2+-5*-5*((5/4+2)))"));
}
}
How about something like this:
String st = "10+3";
int result;
for(int i=0;i<st.length();i++)
{
if(st.charAt(i)=='+')
{
result=Integer.parseInt(st.substring(0, i))+Integer.parseInt(st.substring(i+1, st.length()));
System.out.print(result);
}
}
and do the similar thing for every other mathematical operator accordingly ..
It is possible to convert any expression string in infix notation to a postfix notation using Djikstra's shunting-yard algorithm. The result of the algorithm can then serve as input to the postfix algorithm with returns the result of the expression.
I wrote an article about it here, with an implementation in java
Yet another option: https://github.com/stefanhaustein/expressionparser
I have implemented this to have a simple but flexible option to permit both:
Immediate processing (Calculator.java, SetDemo.java)
Building and processing a parse tree (TreeBuilder.java)
The TreeBuilder linked above is part of a CAS demo package that does symbolic derivation. There is also a BASIC interpreter example and I have started to build a TypeScript interpreter using it.
External library like RHINO or NASHORN can be used to run javascript. And javascript can evaluate simple formula without parcing the string. No performance impact as well if code is written well.
Below is an example with RHINO -
public class RhinoApp {
private String simpleAdd = "(12+13+2-2)*2+(12+13+2-2)*2";
public void runJavaScript() {
Context jsCx = Context.enter();
Context.getCurrentContext().setOptimizationLevel(-1);
ScriptableObject scope = jsCx.initStandardObjects();
Object result = jsCx.evaluateString(scope, simpleAdd , "formula", 0, null);
Context.exit();
System.out.println(result);
}
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class test2 {
public static void main(String[] args) throws ScriptException {
String s = "10+2";
ScriptEngineManager mn = new ScriptEngineManager();
ScriptEngine en = mn.getEngineByName("js");
Object result = en.eval(s);
System.out.println(result);
}
}
I have done using iterative parsing and shunting Yard algorithm and i have really enjoyed developing the expression evaluator ,you can find all the code here
https://github.com/nagaraj200788/JavaExpressionEvaluator
Has 73 test cases and even works for Bigintegers,Bigdecimals
supports all relational, arithmetic expression and also combination of both .
even supports ternary operator .
Added enhancement to support signed numbers like -100+89 it was intresting, for details check TokenReader.isUnaryOperator() method and i have updated code in above Link
Suppose I have an app (in debug/release build , made by me or not), which has an ID for a specific view.
Is it possible to call adb command to click on this view?
I know it's possible to make it click on a specific coordinate, but is it possible to use the ID instead ?
I ask this because I do know that the "Layout Inspector" tool (available via Android Studio) and the "View hierarchy" tool (available via "Android Device Monitor", previously used via DDMS) can show the ids of the views (and even their coordinates and bounding box), so maybe it can be a better way to simulate touches when performing some automatic tests.
I can use a rooted method if needed.
EDIT: I've set a bounty in case there is an easier/better way than what I've written in my own answer, which was to parse the result of "adb shell dumpsys activity top" .
I would like to know if it's possible to get the views coordinates (and sizes of course) that are shown on the screen, including as much information about them (to identify each).
This should be possible via the device too. Maybe something that has the same output data of what's available from the "monitor" tool :
Notice how it can get the basic information of the views, including the text, the id, and the bounds of each.
As I've read, this might be possible via AccessibilityService, but sadly I can't understand how it all works, what are its capabilities, how to trigger it, what are its requirements, etc...
Using what #pskink explained in the comments above, here's how I achieved this:
First, I ran this command:
adb shell dumpsys activity top
Then, I used this code to parse it:
public class ViewCoordsGetter {
public static Rect getViewBoundyingBox(String viewIdStr) {
final List<String> viewHierarchyLog = //result of the command
for (int i = 0; i < viewHierarchyLog.size(); ++i) {
String line = viewHierarchyLog.get(i);
if (line.contains(":id/" + viewIdStr + "}")) {
Rect result = getBoundingBoxFromLine(line);
if (i == 0)
return result;
int currentLineStart = getStartOfViewDetailsInLine(line);
for (int j = i - 1; j >= 0; --j) {
line = viewHierarchyLog.get(j);
if ("View Hierarchy:".equals(line.trim()))
break;
int newLineStart = getStartOfViewDetailsInLine(line);
if (newLineStart < currentLineStart) {
final Rect boundingBoxFromLine = getBoundingBoxFromLine(line);
result.left += boundingBoxFromLine.left;
result.right += boundingBoxFromLine.left;
result.top += boundingBoxFromLine.top;
result.bottom += boundingBoxFromLine.top;
currentLineStart = newLineStart;
}
}
return result;
}
}
return null;
}
private static int getStartOfViewDetailsInLine(String s) {
int i = 0;
while (true)
if (s.charAt(i++) != ' ')
return --i;
}
private static Rect getBoundingBoxFromLine(String line) {
int endIndex = line.indexOf(',', 0);
int startIndex = endIndex - 1;
while (!Character.isSpaceChar(line.charAt(startIndex - 1)))
--startIndex;
int left = Integer.parseInt(line.substring(startIndex, endIndex));
startIndex = endIndex + 1;
endIndex = line.indexOf('-', startIndex);
endIndex = line.charAt(endIndex - 1) == ',' ? line.indexOf('-', endIndex + 1) : endIndex;
int top = Integer.parseInt(line.substring(startIndex, endIndex));
startIndex = endIndex + 1;
endIndex = line.indexOf(',', startIndex);
int right = Integer.parseInt(line.substring(startIndex, endIndex));
startIndex = endIndex + 1;
//noinspection StatementWithEmptyBody
for (endIndex = startIndex + 1; Character.isDigit(line.charAt(endIndex)); ++endIndex)
;
int bot = Integer.parseInt(line.substring(startIndex, endIndex));
return new Rect(left, top, right, bot);
}
}
invalid conversion from 'byte*' to 'byte'
i have write this arduino function
byte receiveMessage(AndroidAccessory acc,boolean accstates){
if(accstates){
byte rcvmsg[255];
int len = acc.read(rcvmsg, sizeof(rcvmsg), 1);
if (len > 0) {
if (rcvmsg[0] == COMMAND_TEXT) {
if (rcvmsg[1] == TARGET_DEFAULT){
byte textLength = rcvmsg[2];
int textEndIndex = 3 + textLength;
byte theMessage[textLength];
int i=0;
for(int x = 3; x < textEndIndex; x++) {
theMessage[i]=rcvmsg[x];
i++;
delay(250);
}
return theMessage;
delay(250);
}
}
}
}
}
this is the error
In function byte receiveMessage(AndroidAccessory, boolean) invalid conversion from byte*' to 'byte"
this function is to receive the data from the android and return it as a byte array
You need to use dynamic allocation, or pass the array to the function as a parameter which is a better solution in your case
void receiveMessage(AndroidAccessory acc, boolean accstates, byte *theMessage){
if (theMessage == NULL)
return;
if(accstates){
byte rcvmsg[255];
int len = acc.read(rcvmsg, sizeof(rcvmsg), 1);
if (len > 0) {
if (rcvmsg[0] == COMMAND_TEXT) {
if (rcvmsg[1] == TARGET_DEFAULT){
byte textLength = rcvmsg[2];
int textEndIndex = 3 + textLength;
int i=0;
for(int x = 3; x < textEndIndex; x++) {
theMessage[i]=rcvmsg[x];
i++;
delay(250);
}
return;
}
}
}
}
}
with this, you will call the function passing the array to it, for example
byte theMessage[255];
receiveMessage(acc, accstates, theMessage);
/* here the message already contains the data you read in the function */
But you can't return a local variable, because the data is only valid in the scope where the variable is valid, in fact it's invalid right outside the if (rcvmsg[0] == COMMAND_TEXT) block, because you defined it local to that block.
Note: please read Wimmel's comment, or you could set the last byte to '\0' if it's just text, and then use the array as a string.
As far as the error is concerned you are returning the incorrect value.
theMessage is a byte array not a byte
Also the last answers explains why cant you return local variable pointer
okay so I'm currently working on an android application in eclipse using the android sdk and adt etc. while I'm compiling my code I'm getting errors I've racked my brain for days looked up similar threads on here before posting but none suited my satisfaction as I tried the various solutions similar to my problem.
being used in eclipse for compiling:
Toolchain: android GCC
Builder: android builder
GNU C++
here are the errors I'm getting:
jni/src/SDL/debugger.cpp:436:21: error: format not a string literal and no format arguments [-Werror=format-security]
printf(m->name);
^
jni/src/SDL/debugger.cpp: In function 'void debuggerMemoryByte(int, char**)':
jni/src/SDL/debugger.cpp:1292:11: error: redeclaration of 'int i'
int i = debuggerReadByte(addr+8);
^
jni/src/SDL/debugger.cpp:1283:13: error: 'int i' previously declared here
for(int i = 0; i < 16; i++) {
^
jni/src/SDL/debugger.cpp: In function 'void debuggerMemoryHalfWord(int, char**)':
jni/src/SDL/debugger.cpp:1328:11: error: redeclaration of 'int i'
int i = debuggerReadByte(addr+8);
^
jni/src/SDL/debugger.cpp:1319:13: error: 'int i' previously declared here
for(int i = 0; i < 16; i++) {
^
jni/src/SDL/debugger.cpp: In function 'void debuggerMemory(int, char**)':
jni/src/SDL/debugger.cpp:1366:11: error: redeclaration of 'int i'
int i = debuggerReadByte(addr+8);
^
jni/src/SDL/debugger.cpp:1355:13: error: 'int i' previously declared here
for(int i = 0; i < 16; i++) {
^
jni/src/SDL/debugger.cpp: In function 'void debuggerOutput(char*, u32)':
jni/src/SDL/debugger.cpp:1403:13: error: format not a string literal and no format arguments [-Werror=format-security]
printf(s);
^
cc1plus.exe: some warnings being treated as errors
make.exe: *** [obj/local/armeabi/objs/main/SDL/debugger.o] Error 1
here is the some of the code sample:
for(int i = 0; i < 16; i++) {
int a = debuggerReadByte(addr);
int b = debuggerReadByte(addr+1);
int c = debuggerReadByte(addr+2);
int d = debuggerReadByte(addr+3);
int e = debuggerReadByte(addr+4);
int f = debuggerReadByte(addr+5);
int g = debuggerReadByte(addr+6);
int h = debuggerReadByte(addr+7);
int i = debuggerReadByte(addr+8);
int j = debuggerReadByte(addr+9);
int k = debuggerReadByte(addr+10);
int l = debuggerReadByte(addr+11);
int m = debuggerReadByte(addr+12);
int n = debuggerReadByte(addr+13);
int o = debuggerReadByte(addr+14);
int p = debuggerReadByte(addr+15);
printf("%08x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n",
addr,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,
ASCII(a),ASCII(b),ASCII(c),ASCII(d),
ASCII(e),ASCII(f),ASCII(g),ASCII(h),
ASCII(i),ASCII(j),ASCII(k),ASCII(l),
ASCII(m),ASCII(n),ASCII(o),ASCII(p));
addr += 16;
}
} else
debuggerUsage("mb");
}
void debuggerMemoryHalfWord(int n, char **args)
{
if(n == 2) {
u32 addr = 0;
sscanf(args[1], "%x", &addr);
addr = addr & 0xfffffffe;
for(int i = 0; i < 16; i++) {
int a = debuggerReadByte(addr);
int b = debuggerReadByte(addr+1);
int c = debuggerReadByte(addr+2);
int d = debuggerReadByte(addr+3);
int e = debuggerReadByte(addr+4);
int f = debuggerReadByte(addr+5);
int g = debuggerReadByte(addr+6);
int h = debuggerReadByte(addr+7);
int i = debuggerReadByte(addr+8);
int j = debuggerReadByte(addr+9);
int k = debuggerReadByte(addr+10);
int l = debuggerReadByte(addr+11);
int m = debuggerReadByte(addr+12);
int n = debuggerReadByte(addr+13);
int o = debuggerReadByte(addr+14);
int p = debuggerReadByte(addr+15);
also it appears that also getting these errors as well, Function 'sscanf' could not be resolved, and Function 'printf' could not be resolved. which bugs me.
I Have included all the header files etc. but not really sure where to go from there I hope someone here can let me know exactly whats going on with the errors stated above, and thanks for the help.
to me the code looks correct but not sure where it maybe wrong.
The problem is that you are declaring the variable "int i" twice -
Once when creating the loop int i = 0 and once inside it int i = debuggerReadByte(addr+8);.
You can solve it by changing the name of one of the variables like for(int z = 0; z < 16; z++).
error: redeclaration of 'int i'
You can simply rename the loop control variable to something else, since you do not use it in the loop anyway.
error: format not a string literal and no format arguments [-Werror=format-security]
You can avoid this by using fputs() or puts() (but that appends a new line, IIRC) instead.
also it appears that also getting these errors as well, Function 'sscanf' could not be resolved, and Function 'printf' could not be resolved. which bugs me.
Try including <cstdio> and use std:: prefix. Your code snippet does not show the includes.
I am trying to use the overlay filter with multiple input sources, for an Android app. Basically, I want to overlay multiple video sources on top of a static image.
I have looked at the sample that comes with ffmpeg and implemented my code based on that, but things don't seem to be working as expected.
In the ffmpeg filtering sample there seems to be a single video input. I have to handle multiple video inputs and I am not sure that my solution is the correct one. I have tried to find other examples, but looks like this is the only one.
Here is my code:
AVFilterContext **inputContexts;
AVFilterContext *outputContext;
AVFilterGraph *graph;
int initFilters(AVFrame *bgFrame, int inputCount, AVCodecContext **codecContexts, char *filters)
{
int i;
int returnCode;
char args[512];
char name[9];
AVFilterInOut **graphInputs = NULL;
AVFilterInOut *graphOutput = NULL;
AVFilter *bufferSrc = avfilter_get_by_name("buffer");
AVFilter *bufferSink = avfilter_get_by_name("buffersink");
graph = avfilter_graph_alloc();
if(graph == NULL)
return -1;
//allocate inputs
graphInputs = av_calloc(inputCount + 1, sizeof(AVFilterInOut *));
for(i = 0; i <= inputCount; i++)
{
graphInputs[i] = avfilter_inout_alloc();
if(graphInputs[i] == NULL)
return -1;
}
//allocate input contexts
inputContexts = av_calloc(inputCount + 1, sizeof(AVFilterContext *));
//first is the background
snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=1/1:pixel_aspect=0", bgFrame->width, bgFrame->height, bgFrame->format);
returnCode = avfilter_graph_create_filter(&inputContexts[0], bufferSrc, "background", args, NULL, graph);
if(returnCode < 0)
return returnCode;
graphInputs[0]->filter_ctx = inputContexts[0];
graphInputs[0]->name = av_strdup("background");
graphInputs[0]->next = graphInputs[1];
//allocate the rest
for(i = 1; i <= inputCount; i++)
{
AVCodecContext *codecCtx = codecContexts[i - 1];
snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
codecCtx->width, codecCtx->height, codecCtx->pix_fmt,
codecCtx->time_base.num, codecCtx->time_base.den,
codecCtx->sample_aspect_ratio.num, codecCtx->sample_aspect_ratio.den);
snprintf(name, sizeof(name), "video_%d", i);
returnCode = avfilter_graph_create_filter(&inputContexts[i], bufferSrc, name, args, NULL, graph);
if(returnCode < 0)
return returnCode;
graphInputs[i]->filter_ctx = inputContexts[i];
graphInputs[i]->name = av_strdup(name);
graphInputs[i]->pad_idx = 0;
if(i < inputCount)
{
graphInputs[i]->next = graphInputs[i + 1];
}
else
{
graphInputs[i]->next = NULL;
}
}
//allocate outputs
graphOutput = avfilter_inout_alloc();
returnCode = avfilter_graph_create_filter(&outputContext, bufferSink, "out", NULL, NULL, graph);
if(returnCode < 0)
return returnCode;
graphOutput->filter_ctx = outputContext;
graphOutput->name = av_strdup("out");
graphOutput->next = NULL;
graphOutput->pad_idx = 0;
returnCode = avfilter_graph_parse_ptr(graph, filters, graphInputs, &graphOutput, NULL);
if(returnCode < 0)
return returnCode;
returnCode = avfilter_graph_config(graph, NULL);
return returnCode;
return 0;
}
The filters argument of the function is passed on to avfilter_graph_parse_ptr and it can looks like this: [background] scale=512x512 [base]; [video_1] scale=256x256 [tmp_1]; [base][tmp_1] overlay=0:0 [out]
The call breaks after the call to avfilter_graph_config with the warning:
Output pad "default" with type video of the filter instance "background" of buffer not connected to any destination and the error Invalid argument.
What is it that I am not doing correctly?
EDIT: The are two issues that I have discovered:
Looks like the description of avfilter_graph_parse_ptr is a bit vague. The ouputs parameter represents a list of the current outputs of the graph, in my case that being the graphInputs variable, because these are the outputs from the buffer filter. The inputs parameter represents a list of the current inputs of the graph, in this case this is the graphOutput variable, because it represents the input to the buffersink filter.
I did some testing with a scale filter and a single input. It seems that the name of the AVFilterInOut structure required by avfilter_graph_parse_ptr needs to be in. I have tried with different versions: in_1, in_link_1. None of them work and I have not been able to find any documentation related to this.
So the issue still remains. How do I implement a filter graph with multiple inputs?
I have found a simple solution to the problem.
This involves replacing the avfilter_graph_parse_ptr with avfilter_graph_parse2 and adding the buffer and buffersink filters to the filters parameter of avfilter_graph_parse2.
So, in the simple case where you have one background image and one input video the value of the filters parameter should look like this:
buffer=video_size=1024x768:pix_fmt=2:time_base=1/25:pixel_aspect=3937/3937 [in_1]; buffer=video_size=1920x1080:pix_fmt=0:time_base=1/180000:pixel_aspect=0/1 [in_2]; [in_1] [in_2] overlay=0:0 [result]; [result] buffersink
The avfilter_graph_parse2 will make all the graph connections and initialize all the filters. The filter contexts for the input buffers and for the output buffer can be retrieved from the graph itself at the end. These are used to add/get frames from the filter graph.
A simplified version of the code looks like this:
AVFilterContext **inputContexts;
AVFilterContext *outputContext;
AVFilterGraph *graph;
int initFilters(AVFrame *bgFrame, int inputCount, AVCodecContext **codecContexts)
{
int i;
int returnCode;
char filters[1024];
AVFilterInOut *gis = NULL;
AVFilterInOut *gos = NULL;
graph = avfilter_graph_alloc();
if(graph == NULL)
{
printf("Cannot allocate filter graph.");
return -1;
}
//build the filters string here
// ...
returnCode = avfilter_graph_parse2(graph, filters, &gis, &gos);
if(returnCode < 0)
{
cs_printAVError("Cannot parse graph.", returnCode);
return returnCode;
}
returnCode = avfilter_graph_config(graph, NULL);
if(returnCode < 0)
{
cs_printAVError("Cannot configure graph.", returnCode);
return returnCode;
}
//get the filter contexts from the graph here
return 0;
}
I cant add a comment so i would just like to add you can fix "Output pad "default" with type video of the filter instance "background" of buffer not connected to any destination" by not having a sink at all. The filter will automatically make the sink for you. So you are adding too many pads
For my case I had a transformation like this:
[0:v]pad=1008:734:144:0:black[pad];[pad][1:v]overlay=0:576[out]
If you try ffmpeg from command line, it will work:
ffmpeg -i first.mp4 -i second.mp4 -filter_complex "[0:v]pad=1008:734:144:0:black[pad];[pad][1:v]overlay=0:576[out]" -map "[out]" -map 0:a output.mp4
Basically, increasing the overall size of first video, then overlapping the second one. After a long try, same problems as this thread, I got it working. The video filtering example from FFMPEG documentation (https://ffmpeg.org/doxygen/2.1/doc_2examples_2filtering_video_8c-example.html) works fine, and after digging into it, this went fine:
filterGraph = avfilter_graph_alloc();
NULLC(filterGraph);
bufferSink = avfilter_get_by_name("buffersink");
NULLC(bufferSink);
filterInput = avfilter_inout_alloc();
AVBufferSinkParams* buffersinkParams = av_buffersink_params_alloc();
buffersinkParams->pixel_fmts = pixelFormats;
FFMPEGHRC(avfilter_graph_create_filter(&bufferSinkContext, bufferSink, "out", NULL, buffersinkParams, filterGraph));
av_free(buffersinkParams);
filterInput->name = av_strdup("out");
filterInput->filter_ctx = bufferSinkContext;
filterInput->pad_idx = 0;
filterInput->next = NULL;
filterOutputs = new AVFilterInOut*[inputFiles.size()];
ZeroMemory(filterOutputs, sizeof(AVFilterInOut*) * inputFiles.size());
bufferSourceContext = new AVFilterContext*[inputFiles.size()];
ZeroMemory(bufferSourceContext, sizeof(AVFilterContext*) * inputFiles.size());
for (i = inputFiles.size() - 1; i >= 0 ; i--)
{
snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
videoCodecContext[i]->width, videoCodecContext[i]->height, videoCodecContext[i]->pix_fmt, videoCodecContext[i]->time_base.num, videoCodecContext[i]->time_base.den, videoCodecContext[i]->sample_aspect_ratio.num, videoCodecContext[i]->sample_aspect_ratio.den);
filterOutputs[i] = avfilter_inout_alloc();
NULLC(filterOutputs[i]);
bufferSource = avfilter_get_by_name("buffer");
NULLC(bufferSource);
sprintf(args2, outputTemplate, i);
FFMPEGHRC(avfilter_graph_create_filter(&bufferSourceContext[i], bufferSource, "in", args, NULL, filterGraph));
filterOutputs[i]->name = av_strdup(args2);
filterOutputs[i]->filter_ctx = bufferSourceContext[i];
filterOutputs[i]->pad_idx = 0;
filterOutputs[i]->next = i < inputFiles.size() - 1 ? filterOutputs[i + 1] : NULL;
}
FFMPEGHRC(avfilter_graph_parse_ptr(filterGraph, description, &filterInput, filterOutputs, NULL));
FFMPEGHRC(avfilter_graph_config(filterGraph, NULL));
The type of variables are the same as in example above, the args and args2 are char[512], where outputTemplate is "%d:v", basically the input video IDs from filtering expression. Couple of things to watch-out:
The video information in args, needs to be correct, time_base and sample_aspect_ration are copied from the video stream of format context.
Indeed the input, is what is for us output, and the other way around
The name of the filter is "in" for all our input filters(filterOutputs)