我正在为我们的应用程序支持kindle设备上的通知。我们将从包含主题的服务器接收表达式sting(主题将用单引号括起来)。我们需要通过检查设备是否订阅了主题来验证表达式。下面是逻辑表达式的示例。
('18' in topics || '4' in topics) && ('REG_92779' in topics || 'REG_91212' in topics)
假设设备已订阅主题“18”和“4”,而未订阅其他主题,则通知对设备无效。如果设备订阅了主题“18”和“reg\ U 91212”,则通知对设备有效。我写了下面的代码,它是工作良好。
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()函数将主题替换为1或0(取决于设备是否订阅),修剪空格,删除引号,将“| |”替换为“|”,将“&&”替换为“&”。。。将缩短的字符串发送到evaluateTopiceExpression()之前。
例如,如果逻辑表达式
('18' in topics || '4' in topics) && ('REG_92779' in topics || 'REG_91212' in topics)
并且设备订阅了主题“18”、“4”、“reg\u 92779”,checknotificationvalid()调用evaluateTopiceExpression(),使用以下字符串
(1|1)&(1|0)
我测试了很多表达式,代码运行良好。尽管代码运行良好,但我觉得evaluatetopicexpression()有很多代码,而且它不是高效的代码,因为我正在遍历每个字符并递归调用函数。不管怎样,我们可以减少代码并以更有效的方式计算表达式。
注意:我们可以用true或false替换代码,而不是用1或0替换代码
(true||true)&&(true||false)
我需要做的就是通知是否对设备有效。
1条答案
按热度按时间lrl1mhuk1#
在同一个递归函数中进行解析和求值
boolean evalExpression(String expr)
. 这样可以避免递归到冗余树中。如果计算的表达式中有&&运算符的一半为false,则不需要计算另一半。