当前位置:七道奇文章资讯编程技术Java编程
日期: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%(0)

    文章评论评论内容只代表网友观点,与本站立场无关!

       评论摘要(共 0 条,得分 0 分,平均 0 分) 查看完整评论
    Copyright © 2020-2022 www.xiamiku.com. All Rights Reserved .