日期:2011-03-22 16:16:00 来源:本站整理
查抄大小写格局[Java编程]
本文“查抄大小写格局[Java编程]”是由七道奇为您精心收集,来源于网络转载,文章版权归文章作者所有,本站不对其观点以及内容做任何评价,请读者自行判断,以下是其具体内容:
固然对触及文字处理的一些项目来说,前例显得对比便利,但下面要介绍的项目却能当即施展作用,因为它履行的是一个款式查抄,以确保我们的大小写情势符合“事实上”的Java款式尺度.它会在当前目录中翻开每个.java文件,并提取出全部类名以及标识符.若发现有不符合Java款式的情形,就向我们提出报告.
为了让这个程序精确运行,首先必须构建一个类名,将它作为一个“仓库”,负责包容尺度Java库中的全部类名.为到达这个目的,需遍历用于尺度Java库的全部源码子目录,并在每个子目录都运行ClassScanner.至于参数,则供应仓库文件的名字(每次都用相同的途径和名字)和号令行开关-a,指出类名该当增添到该仓库文件中.
为了用程序查抄自己的代码,需求运行它,并向它传送要利用的仓库文件的途径与名字.它会查抄当前目录中的全部类和标识符,并奉告我们哪些没有服从典型的Java大写写标准.
要注意这个程序并非十全十美的.有些时刻,它大概报告自己查到一个问题.但当我们细心查抄代码的时刻,却发现没有什么需求更改的.固然这有点儿烦人,但仍比自己着手查抄代码中的全部错误强得多.
下面列出源代码,背面有具体的注释:
//: ClassScanner.java // Scans all files in directory for classes // and identifiers, to check capitalization. // Assumes properly compiling code listings. // Doesn't do everything right, but is a very // useful aid. import java.io.*; import java.util.*; class MultiStringMap extends Hashtable { public void add(String key, String value) { if(!containsKey(key)) put(key, new Vector()); ((Vector)get(key)).addElement(value); } public Vector getVector(String key) { if(!containsKey(key)) { System.err.println( "ERROR: can't find key: " + key); System.exit(1); } return (Vector)get(key); } public void printValues(PrintStream p) { Enumeration k = keys(); while(k.hasMoreElements()) { String oneKey = (String)k.nextElement(); Vector val = getVector(oneKey); for(int i = 0; i < val.size(); i++) p.println((String)val.elementAt(i)); } } } public class ClassScanner { private File path; private String[] fileList; private Properties classes = new Properties(); private MultiStringMap classMap = new MultiStringMap(), identMap = new MultiStringMap(); private StreamTokenizer in; public ClassScanner() { path = new File("."); fileList = path.list(new JavaFilter()); for(int i = 0; i < fileList.length; i++) { System.out.println(fileList[i]); scanListing(fileList[i]); } } void scanListing(String fname) { try { in = new StreamTokenizer( new BufferedReader( new FileReader(fname))); // Doesn't seem to work: // in.slashStarComments(true); // in.slashSlashComments(true); in.ordinaryChar('/'); in.ordinaryChar('.'); in.wordChars('_', '_'); in.eolIsSignificant(true); while(in.nextToken() != StreamTokenizer.TT_EOF) { if(in.ttype == '/') eatComments(); else if(in.ttype == StreamTokenizer.TT_WORD) { if(in.sval.equals("class") || in.sval.equals("interface")) { // Get class name: while(in.nextToken() != StreamTokenizer.TT_EOF && in.ttype != StreamTokenizer.TT_WORD) ; classes.put(in.sval, in.sval); classMap.add(fname, in.sval); } if(in.sval.equals("import") || in.sval.equals("package")) discardLine(); else // It's an identifier or keyword identMap.add(fname, in.sval); } } } catch(IOException e) { e.printStackTrace(); } } void discardLine() { try { while(in.nextToken() != StreamTokenizer.TT_EOF && in.ttype != StreamTokenizer.TT_EOL) ; // Throw away tokens to end of line } catch(IOException e) { e.printStackTrace(); } } // StreamTokenizer's comment removal seemed // to be broken. This extracts them: void eatComments() { try { if(in.nextToken() != StreamTokenizer.TT_EOF) { if(in.ttype == '/') discardLine(); else if(in.ttype != '*') in.pushBack(); else while(true) { if(in.nextToken() == StreamTokenizer.TT_EOF) break; if(in.ttype == '*') if(in.nextToken() != StreamTokenizer.TT_EOF && in.ttype == '/') break; } } } catch(IOException e) { e.printStackTrace(); } } public String[] classNames() { String[] result = new String[classes.size()]; Enumeration e = classes.keys(); int i = 0; while(e.hasMoreElements()) result[i++] = (String)e.nextElement(); return result; } public void checkClassNames() { Enumeration files = classMap.keys(); while(files.hasMoreElements()) { String file = (String)files.nextElement(); Vector cls = classMap.getVector(file); for(int i = 0; i < cls.size(); i++) { String className = (String)cls.elementAt(i); if(Character.isLowerCase( className.charAt(0))) System.out.println( "class capitalization error, file: " + file + ", class: " + className); } } } public void checkIdentNames() { Enumeration files = identMap.keys(); Vector reportSet = new Vector(); while(files.hasMoreElements()) { String file = (String)files.nextElement(); Vector ids = identMap.getVector(file); for(int i = 0; i < ids.size(); i++) { String id = (String)ids.elementAt(i); if(!classes.contains(id)) { // Ignore identifiers of length 3 or // longer that are all uppercase // (probably static final values): if(id.length() >= 3 && id.equals( id.toUpperCase())) continue; // Check to see if first char is upper: if(Character.isUpperCase(id.charAt(0))){ if(reportSet.indexOf(file + id) == -1){ // Not reported yet reportSet.addElement(file + id); System.out.println( "Ident capitalization error in:" + file + ", ident: " + id); } } } } } } static final String usage = "Usage: \n" + "ClassScanner classnames -a\n" + "\tAdds all the class names in this \n" + "\tdirectory to the repository file \n" + "\tcalled 'classnames'\n" + "ClassScanner classnames\n" + "\tChecks all the java files in this \n" + "\tdirectory for capitalization errors, \n" + "\tusing the repository file 'classnames'"; private static void usage() { System.err.println(usage); System.exit(1); } public static void main(String[] args) { if(args.length < 1 || args.length > 2) usage(); ClassScanner c = new ClassScanner(); File old = new File(args[0]); if(old.exists()) { try { // Try to open an existing // properties file: InputStream oldlist = new BufferedInputStream( new FileInputStream(old)); c.classes.load(oldlist); oldlist.close(); } catch(IOException e) { System.err.println("Could not open " + old + " for reading"); System.exit(1); } } if(args.length == 1) { c.checkClassNames(); c.checkIdentNames(); } // Write the class names to a repository: if(args.length == 2) { if(!args[1].equals("-a")) usage(); try { BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream(args[0])); c.classes.save(out, "Classes found by ClassScanner.java"); out.close(); } catch(IOException e) { System.err.println( "Could not write " + args[0]); System.exit(1); } } } } class JavaFilter implements FilenameFilter { public boolean accept(File dir, String name) { // Strip path information: String f = new File(name).getName(); return f.trim().endsWith(".java"); } } ///:~
MultiStringMap类是个特别的工具,答应我们将一组字串与每个键项对应(映射)起来.和前例一样,这里也利用了一个散列表(Hashtable),不过这次设置了担当.该散列表将键作为映射成为Vector值的单一的字串对待.add()办法的作用很简单,负责查抄散列表里能否存在一个键.假如不存在,就在此中安排一个.getVector()办法为一个特定的键产生一个Vector;而printValues()将全部值一一Vector地打印出来,这对程序的调试非常有效.
为简化程序,来自尺度Java库的类名全都置入一个Properties(属性)对象中(来自尺度Java库).记着Properties对象实际是个散列表,此中只包容了用于键和值项的String对象.但是仅需一次办法调用,我们便可把它保存到磁盘,大概从磁盘中恢复.实际上,我们只需求一个名字列表,所认为键和值都利用了相同的对象.
针对特定目录中的文件,为找出呼应的类与标识符,我们利用了两个MultiStringMap:classMap以及identMap.此外在程序启动的时刻,它会将尺度类名仓库装载到名为classes的Properties对象中.一旦在本地目录发现了一个新类名,也会将其加入classes以及classMap.这样一来,classMap便可用于在本地目录的全部类间遍历,并且可用classes查抄当前标志是不是一个类名(它标志着对象或办法定义的开始,所以汇集接下去的暗号——直到碰到一个分号——并将它们都置入identMap).
ClassScanner的默许构建器会成立一个由文件名构成的列表(采取FilenameFilter的JavaFilter实现情势,拜见第10章).随后会为每个文件名都调用scanListing().
在scanListing()内部,会翻开源码文件,并将其转换成一个StreamTokenizer.按照Java帮忙文档,将true传送给slashStartComments()和slashSlashComments()的本意该当是剥除那些注释内容,但这样做仿佛有些问题(在Java 1.0中几近无效).所以相反,那些行被当作注释标志出去,并用另一个办法来提取注释.为到达这个目的,'/'必须作为一个原始字符捕捉,而不是让StreamTokeinzer将其当作注释的一部份对待.此时要用ordinaryChar()办法指导StreamTokenizer采纳精确的操作.一样的原理也实用于点号('.'),因为我们但愿让办法调用别离出单独的标识符.但对下划线来说,它最初是被StreamTokenizer当作一个单独的字符对待的,但此时应把它留作标识符的一部份,因为它在static final值中以TT_EOF等等情势利用.当然,这一点只对目前这个特别的程序成立.wordChars()办法需求获得我们想增添的一系列字符,把它们留在作为一个单词对待的暗号中.最后,在解析单行注释大概放弃一行的时刻,我们需求知道一个换行行动什么时刻发生.所以通过调用eollsSignificant(true),换行符(EOL)会被显示出来,而不是被StreamTokenizer吸取.
scanListing()剩余的部份将读入和查抄暗号,直至文件尾.一旦nextToken()返回一个final static值——StreamTokenizer.TT_EOF,就标志着已经到达文件尾部.
若暗号是个'/',意味着它大概是个注释,所以就调用eatComments(),对这种情形举行处理.我们在这儿唯一感爱好的其他情形是它能否为一个单词,当然还大概存在另一些特别情形.
假如单词是class(类)或interface(接口),那么接着的暗号就应现代表一个类或接口名字,并将其置入classes和classMap.若单词是import大概package,那么我们对这一行剩下的东西就没什么爱好了.其他全部东西必定是一个标识符(这是我们感爱好的),大概是一个关键字(对此不感爱好,但它们采取的必定是小写情势,所以没必要发兵动众地查抄它们).它们将加入到identMap.
discardLine()办法是一个简单的工具,用于查找行末位置.注意每次得到一个新暗号时,都必须查抄行末.
只要在主解析循环中碰到一个正斜杠,就会调用eatComments()办法.但是,这并不表示必定碰到了一条注释,所以必须将接着的暗号提取出来,查抄它是一个正斜杠(那么这一行会被丢弃),还是一个星号.但假定二者都不是,意味着必须在主解析循环中将方才取出的暗号送回去!幸运的是,pushBack()办法答应我们将当前暗号“压回”输入数据流.所以在主解析循环调用nextToken()的时刻,它能精确地得到方才送回的东西.
为便利起见,classNames()办法产生了一个数组,此中包含了classes调集合的全部名字.这个办法未在程序中利用,但对代码的调试非常有效.
接下来的两个办法是实际举行查抄的地方.在checkClassNames()中,类名从classMap提取出来(请记着,classMap只包含了这个目录内的名字,它们按文件名组织,所以文件名大概伴随错误的类名打印出来).为做到这一点,需求取出每个关联的Vector,并遍历此中,查抄第一个字符能否为小写.若确切为小写,则打印出呼应的出错提醒消息.
在checkIdentNames()中,我们采取了一种近似的办法:每个标识符名字都从identMap中提取出来.假如名字不在classes列表中,就认为它是一个标识符大概关键字.此时会查抄一种特别情形:假如标识符的长度等于3大概更长,并且全部字符都是大写的,则忽视此标识符,因为它大概是一个static final值,比方TT_EOF.当然,这并非一种完善的算法,但它假定我们终究会注意到任何全大写标识符都是不符合的.
这个办法并非报告每一个以大写字符开首的标识符,而是跟踪那些已在一个名为reportSet()的Vector中报告过的.它将Vector当作一个“调集”对待,奉告我们一个项目能否已在那个调集合.该项目是通过将文件名和标识符衔接起来生成的.若元素不在调集合,就加入它,然后产生报告.
程序列表剩下的部份由main()构成,它负责掌握号令行参数,并判断我们是预备在尺度Java库的底子上构建由一系列类名构成的“仓库”,还是想查抄已写好的那些代码的精确性.不管在哪类情形下,城市成立一个ClassScanner对象.
无论预备构建一个“仓库”,还是预备利用一个现成的,都必须尝试翻开现有仓库.通过成立一个File对象并测试能否存在,便可决意能否翻开文件并在ClassScanner中装载classes这个Properties列表(利用load()).来自仓库的类将追加到由ClassScanner构建器发现的类背面,而不是将其覆盖.假如仅供应一个号令行参数,就意味着自己想对类名和标识符名字举行一次查抄.但假定供应两个参数(第二个是"-a"),就表明自己想构成一个类名仓库.在这种情形下,需求翻开一个输出文件,并用Properties.save()办法将列表写入一个文件,同时用一个字串供应文件头信息.
以上是“查抄大小写格局[Java编程]”的内容,如果你对以上该文章内容感兴趣,你可以看看七道奇为您推荐以下文章:
本文地址: | 与您的QQ/BBS好友分享! |
- ·上一篇文章:办法查找工具
- ·下一篇文章:提替换码列表
- ·中查找“查抄大小写格局”更多相关内容
- ·中查找“查抄大小写格局”更多相关内容
评论内容只代表网友观点,与本站立场无关!
评论摘要(共 0 条,得分 0 分,平均 0 分)
查看完整评论