CTS测试框架 -- RegexTrie
来源:互联网 发布:软件项目验收单 编辑:程序博客网 时间:2024/06/13 12:17
概述:前面已经提到,基础框架Trade-Federation默认就支持很多命令。在控制台输入一组命令,肯定要经过解析,然后去执行的过程。基础框架中对于命令有一个单独的数据结构去组织,并不是通过if-else
这样去比较string的。比如:前面提到的命令:run cts --plan cts
,这个命令中,run才是命令,而剩下的都属于参数;再比如:list configs
,还有命令的简写,list
可以简写为l
等等。
1.Trie
Trie树又称为字典树,又叫做单词查找树。主要用于文本检索以及词频统计等。有一个很大的优点就是能够减少无效匹配的次数。
实现方式:从root节点开始,每个节点存储一个字符。每次在向树中存储字符串的时候,根据单词中字符的顺序,逐个向树的每层去存放,如果该层已经有该字符,就去取下一个字符。
例:现在有一些单词:t,tx,txt,to,txa,too,a,b
构建成为字典树如图所示
看这个图就能一目了然,因为共前缀,能够加快查找效率,且查找以及构建的过程可以同时进行。
2.RegexTrie
RegexTrie是在这个基础框架中的一个工具类,实现跟上面的Trie很像,但是还是有区别的:区别就在于RegexTrie的节点上存放的是正则表达式,也就是支持的命令的正则表达式。
基础框架中支持两种命令:定长命令以及变长命令。
定长命令:list configs
– 固定的字符串(包括简写)
变长命令:run cts --plan cts
– 后带变长参数
树的结构:
接下来看下实现:
首先是这个类中的变量:
private V mValue = null;private Map<CompPattern, RegexTrie<V>> mChildren = new LinkedHashMap<CompPattern, RegexTrie<V>>();
2.1 put
public V put(V value, String... regexen) { // 这个地方直接就把输入的正则表达式的参数解析封装为CompPattern // CompPattern内部有一个Pattern变量,不过自己还实现了一些Object中的方法 List<CompPattern> pList = new ArrayList<CompPattern>(regexen.length); for (String regex : regexen) { // 这个地方需要注意,因为参数是变长的,这里如果发现了null // 也把null添加进了数组,而且跳出了循环 // 主要是为了支持变长命令,变长命令在存储的时候最后一个的参数为null if (regex == null) { pList.add(null); break; } Pattern pat = Pattern.compile(regex); // 把所有的正则表达式装进List,准备往树中存放 pList.add(new CompPattern(pat)); } return validateAndPut(value, pList);}private V validateAndPut(V value, List<CompPattern> pList) { if (pList.size() == 0) { throw new IllegalArgumentException("pattern list must be non-empty."); } return recursivePut(value, pList);}V recursivePut(V value, List<CompPattern> patterns) { // Cases: // 1) patterns is empty -- set our value // 2) patterns is non-empty -- recurse downward, creating a child if necessary if (patterns.isEmpty()) { // 如果前面封装的list中已经没有元素了,则把对应的value存放到V上 V oldValue = mValue; mValue = value; return oldValue; } else { // 每次取出数组中的第一个元素 CompPattern curKey = patterns.get(0); List<CompPattern> nextKeys = patterns.subList(1, patterns.size()); // 只要数组中的元素没有取完,就递归继续 RegexTrie<V> nextChild = mChildren.get(curKey); if (nextChild == null) { nextChild = new RegexTrie<V>(); // 树的构建 mChildren.put(curKey, nextChild); } return nextChild.recursivePut(value, nextKeys); }}
这个就是树的构建,使用就在Console
类中:
首先是定长命令:
trie.put(new Runnable() { @Override public void run() { IDeviceManager manager = GlobalConfiguration.getDeviceManagerInstance(); manager.displayDevicesInfo(new PrintWriter(System.out, true)); } }, LIST_PATTERN, "d(?:evices)?");
可见在存放的时候就把是以正则表达式做为参数。
然后是变长命令:
ArgRunnable<CaptureList> runRunCommand = new ArgRunnable<CaptureList>() { @Override public void run(CaptureList args) { // The second argument "command" may also be missing, if the // caller used the shortcut. int startIdx = 1; if (args.get(1).isEmpty()) { // Empty array (that is, not even containing an empty string) means that // we matched and skipped /(?:singleC|c)ommand/ startIdx = 2; } String[] flatArgs = new String[args.size() - startIdx]; for (int i = startIdx; i < args.size(); i++) { flatArgs[i - startIdx] = args.get(i).get(0); } try { mScheduler.addCommand(flatArgs); } catch (ConfigurationException e) { printLine("Failed to run command: " + e.toString()); } }};// 可以看到对于run这个正则表达式,因为这个命令就是为了执行的,肯定是要带参数的// 因此,往树中存放的时候最后一参数为null// 上面put时对于null的判断也就是为了处理这种情况// 这个地方存放的时候存入的V直接就是一个runnable对象了trie.put(runRunCommand, RUN_PATTERN, null);protected static final String RUN_PATTERN = "r(?:un)?";
2.2retrieve
public V retrieve(List<List<String>> captures, String... strings) { if (strings.length == 0) { throw new IllegalArgumentException("string list must be non-empty"); } // 在取的时候就是根据控制台的输入了,同样是变长参数 List<String> sList = Arrays.asList(strings); if (captures != null) { captures.clear(); } return recursiveRetrieve(captures, sList);}// 递归匹配V recursiveRetrieve(List<List<String>> captures, List<String> strings) { if (strings.isEmpty()) { // 如果参数数组已经全部取出,则取出其对应的V return mValue; } else { boolean wildcardMatch = false; V wildcardValue = null; // 每次拿出数组的第一个元素 String curKey = strings.get(0); List<String> nextKeys = strings.subList(1, strings.size()); // 根据参数取出对应的子树 for (Map.Entry<CompPattern, RegexTrie<V>> child : mChildren.entrySet()) { CompPattern pattern = child.getKey(); if (pattern == null) { // 如果发现pattern为null,也就是说存的时候就是变长模式,则启用统配 wildcardMatch = true; wildcardValue = child.getValue().getValue(); continue; } Matcher matcher = pattern.matcher(curKey); // 正则表达式match if (matcher.matches()) { if (captures != null) { List<String> curCaptures = new ArrayList<String>(matcher.groupCount()); for (int i = 0; i < matcher.groupCount(); i++) { // i+1 since group 0 is the entire matched string curCaptures.add(matcher.group(i+1)); } captures.add(curCaptures); } // 递归 return child.getValue().recursiveRetrieve(captures, nextKeys); } } // 如果是通配模式,则返回所有的剩余参数 if (wildcardMatch) { // Stick the rest of the query string into the captures list and return if (captures != null) { for (String str : strings) { captures.add(Arrays.asList(str)); } } return wildcardValue; } return null; }}
可以看到在取的时候正式根据参数的list逐个去和树中每个节点上的正则表达式进行match,如果可以匹配则进入子树继续,直到参数取完,此时拿出对应的V。
3.总结
基础框架中通过这样一个正则表达式字典树的结构,在初始化的时候就把自己支持的命令以及这个命令要执行的action – Runnable对象存进了树,在用户输入参数之后直接去树中取,可以说是取值以及匹配同时进行,直道拿到对应的Runnable对象,取出执行。
虽然这样对于命令的支持很到位,但是这样的数据结构也优缺点,就是对于数据量小的话支持比较到位,但是当数据量很大,那这个树就会很大,效率也就没有那么高了。
- CTS测试框架 -- RegexTrie
- CTS测试框架 -- 开篇
- CTS测试框架 -- 总结
- CTS测试框架 -- 命令解析
- CTS测试框架 -- 命令调度
- CTS测试框架 -- 命令执行
- CTS测试框架 -- V1版本
- CTS测试框架 -- V2版本
- CTS测试框架 -- 基础框架Trade-Federation
- CTS测试框架 -- 基础框架启动
- Android兼容性测试框架(CTS)手册
- ANDROID兼容性测试框架(CTS)手册
- Android兼容性测试框架(CTS)手册
- Android兼容性测试框架(CTS)手册
- Android兼容性测试框架(CTS)手册
- Android兼容性测试框架(CTS)手册
- Android兼容性测试框架(CTS)手册
- cts 测试
- webService一种简单的使用
- 我的lua 学习2
- java代码优化的小建议
- 越越的交通指挥系统 (traffic.pas/c/cpp)
- 彻底理解js中this的指向,不必硬背。
- CTS测试框架 -- RegexTrie
- 最简单的目标跟踪--模版匹配opencv
- Servlet概念(学习)+推荐实践博客
- Microsoft Office 2016简体中文正式版
- php调用另一文件中的类
- CC1101 低功耗(低于1GHz)射频收发器
- Android studio中正确引入so文件的方法
- virtualenv详解及使用virtualenv复制虚拟独立的python环境
- leetcode 516. Longest Palindromic Subsequence 最长回文子序列 + DP动态规划