简体   繁体   English

评估在java中表示为字符串的逻辑表达式

[英]Evaluating logical expression which is represented as string in java

I am working on supporting notifications on kindle devices for our app.我正在为我们的应用程序支持 Kindle 设备上的通知。 We will receive the expression sting from server which contains topics (topics will be in single quotes).我们将从包含主题的服务器接收表达式 sting(主题将在单引号中)。 We need to validate the expression by checking whether device is subscribed to the topics or not.我们需要通过检查设备是否订阅主题来验证表达式。 Below is the example how logical expression looks like.下面是逻辑表达式的示例。

('18' in topics || '4' in topics) && ('REG_92779' in topics || 'REG_91212' in topics)

Assume the device is subscribed to topics '18' and '4' and not subscribed to other topics, the notification is not valid for the device.假设设备订阅了主题“18”和“4”并且没有订阅其他主题,则通知对设备无效。 If the device is subscribed to topics '18' and 'REG_91212', the notification is valid for the device.如果设备订阅了主题“18”和“REG_91212”,则通知对设备有效。 I wrote the below code and it is working fine.我写了下面的代码,它工作正常。

private boolean checkNotificationValid(String leString) {
        // Remove the 'in topics' string. examples of logical expression as below
        // ('4' in topics && '50000' in topics) || ('60' in topics || '3' in topics)
        // ('4' in topics && ('50000' in topics || '60' in topics))
        leString = leString.replaceAll("in topics", "");
        Log.d(TAG, "Logical Expression received : " + leString);
        boolean result = false;

        // Find the topics in the logical expression and check whether the device is subscribed to
        // the topic. If device is subscribed to the topic, replace the topic with 1 or replace the
        // topic with 0. Below is the example.
        // Assume device is subscribed to topics 3 and 4.
        // Expression : ('4' && '50000') || ('60' || '3')
        // After this block of code, it becomes : ('1' && '0') || ('0' || '1')
        StringBuffer buffer = new StringBuffer();
        Pattern p = Pattern.compile("'(.*?)'");
        Matcher m = p.matcher(leString);
        while(m.find()) {
            Log.d(TAG, "LE : " + m.group(1));
            // Check whether the device is subscribed to the topic
            m.appendReplacement(buffer, NotificationUtils.isTopicExistInSharedPref(m.group(1)) ? "1" : "0");
        }

        m.appendTail(buffer);
        leString = buffer.toString();

        // Remove the quotes and spaces from the string, replace '||' with '|', replace '&&' with '&'
        // ('1' && '0') || ('0' || '1') -> (1&0)|(0|1)
        leString = leString.replaceAll("[' ]", "");
        leString = leString.replaceAll("\\|\\|", "|");
        leString = leString.replaceAll("&&", "&");
        Log.d(TAG, "String after changing : " + leString);

        // Evaluate the logical expression
        result = evaluateTopicExpression(leString);

        return result;
    }

    private boolean evaluateTopicExpression(String expString) {
        int len = expString.length();
        int idx = 0;
        boolean result = false;
        int parenCnt = 0;

        // Check for empty expression
        if(len == 0) {
            Log.d(TAG, "empty expression in evaluateTopicExpression");
            return false;
        }

        // If there is only one character it shouldn't be logical operator and shouldn't be
        // parentheses as we are removing parentheses while evaluating the expression.
        if(len == 1) {
            if(expString.charAt(idx) == '0') {
                return false;
            } else {
                return true;
            }
        }

        // Check if the starting character is '('. If it is open parentheses, find the matching
        // closed parentheses. We need to find the exact closed parentheses, extract the string
        // between the parentheses and evaluate that string, concatenate the value to the remaining
        // string and evaluate the string.
        // Example as below.
        // (1|0)&(1&1) -> evaluate 1|0 -> result of 1|0 is 1 -> concatenate the result to remaining
        //                string -> 1&(1&1) -> evaluate the string
        // (1|0)&1 -> evaluate 1|0 -> result of 1|0 is 1 -> concatenate the result to remaining string
        //            -> 1&1 -> evaluate the string
        // (1&(0|1)) -> evaluate 1&(0|1) (This is because once we remove the last parentheses there
        //              is no remaining string after the parentheses)
        if(expString.charAt(idx) == '(') {
            int endIndex = 0;
            parenCnt ++;
            for(int i = idx + 1; i < len; i++) {
                if(expString.charAt(i) == '(') {
                    parenCnt++;
                } else if(expString.charAt(i) == ')') {
                    parenCnt--;
                }
                if(parenCnt == 0) {
                    endIndex = i;
                    break;
                }
            }

            if(parenCnt != 0) {
                Log.d(TAG, "Invalid matching parentheses");
                return false;
            }

            if(endIndex == len - 1) {
                return evaluateTopicExpression(expString.substring(1, endIndex));
            }
            else {
                return evaluateTopicExpression((evaluateTopicExpression(expString.substring(1, endIndex)) ? '1' : '0')
                        + expString.substring(endIndex + 1));
            }
        }

        // If expression string length >= 2, evaluate the first 2 characters in the string and evaluate
        // the remaining string in the next recursive function call
        if(idx + 2 < len) {
            if(expString.charAt(idx + 1) == '|') {
                if(expString.charAt(idx) == '0') {
                    result = evaluateTopicExpression(expString.substring(idx + 2));
                } else {
                    result =  true;
                }
            }

            if(expString.charAt(idx + 1) == '&') {
                if(expString.charAt(idx) == '1') {
                    result = evaluateTopicExpression(expString.substring(idx + 2));
                } else {
                    result = false;
                }
            }
        } else {
            Log.d(TAG, "Error, Invalid logical expression");
            result = false;
        }

        return result;
    }

checkNotificationValid() function replaces topics with 1 or 0 (based on whether device subscribed / not), trims the spaces, remove the quotes, replace '||' checkNotificationValid() 函数用 1 或 0 替换主题(基于设备是否订阅/未订阅),修剪空格,删除引号,替换 '||' with '|', replace '&&' with '&' ... before sending the shortened string to evaluateTopicExpression().使用'|',将'&&' 替换为'&' ... 在将缩短的字符串发送到evaluateTopicExpression() 之前。

For example, if the logical expression is例如,如果逻辑表达式是

('18' in topics || '4' in topics) && ('REG_92779' in topics || 'REG_91212' in topics)

and the device is subscribed to topics '18', '4', 'REG_92779', checkNotificationValid() calls evaluateTopicExpression() with the below string并且设备订阅了主题“18”、“4”、“REG_92779”,checkNotificationValid() 使用下面的字符串调用evaluateTopicExpression()

(1|1)&(1|0)

I tested with lot of expressions and the code is working fine.我测试了很多表达式,代码运行良好。 Even though code is working fine, I feel like evaluateTopicExpression() has lot of code and it is not efficient code as I am going through each character and calling the function recursively.尽管代码运行良好,但我觉得evaluateTopicExpression() 有很多代码并且它不是高效的代码,因为我正在遍历每个字符并递归调用该函数。 Is there anyway we can reduce the code and evaluate the expression in more efficient way.无论如何我们可以减少代码并以更有效的方式评估表达式。

NOTE: Instead of replacing the code with 1 or 0, we can replace with true or false like注意:我们可以用 true 或 false 代替,而不是用 1 或 0 替换代码

(true||true)&&(true||false)

All I need to do is whether the notification is valid for the device or not.我需要做的就是通知是否对设备有效。

Do the parsing an the evaluation in the same recursive function boolean evalExpression(String expr) .在同一个递归函数boolean evalExpression(String expr)进行解析和评估。 This way you can avoid recursion into redundant trees.这样您就可以避免递归到冗余树中。 If you evaluate one half of a expression that has the && operator and it's false you don't need to evaluate the other half.如果您计算具有 && 运算符的表达式的一半并且它是假的,则不需要计算另一半。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM