查抄巨细写样式
当前位置:以往代写 > JAVA 教程 >查抄巨细写样式
2019-06-14

查抄巨细写样式

查抄巨细写样式

尽量对涉及文字处理惩罚的一些项目来说,前例显得较量利便,但下面要先容的项目却能当即发挥浸染,因为它执行的是一个样式查抄,以确保我们的巨细写形式切合“事实上”的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");
  }
} ///:~

#p#分页标题#e#

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()要领将列表写入一个文件,同时用一个字串提供文件头信息。

    关键字:

在线提交作业