簡體   English   中英

如何使用Perl識別對Java類的引用?

[英]How can I identify references to Java classes using Perl?

我正在寫一個Perl腳本,我已經到了一個點,我需要逐行解析Java源文件,檢查對完全限定的Java類名的引用。 我知道我正在尋找的課程; 也是正在搜索的源文件的完全限定名稱(基於其路徑)。

例如,在com / bob / is / YourUncle.java文件中找到對foo.bar.Baz的所有有效引用。

此時我能想到的需要考慮的案例是:

  1. 正在解析的文件與搜索類位於同一個包中。

    在foo / bar / Boing.java中找到foo.bar.Baz引用

  2. 它應該忽略評論。

     // this is a comment saying this method returns a foo.bar.Baz or Baz instance // it shouldn't count /* a multiline comment as well this shouldn't count if I put foo.bar.Baz or Baz in here either */ 
  3. 在線完全限定參考。

     foo.bar.Baz fb = new foo.bar.Baz(); 
  4. 引用基於import語句。

     import foo.bar.Baz; ... Baz b = new Baz(); 

在Perl 5.8中最有效的方法是什么? 有些花哨的正則表達式可能嗎?

open F, $File::Find::name or die;
# these three things are already known
# $classToFind    looking for references of this class
# $pkgToFind      the package of the class you're finding references of
# $currentPkg     package name of the file being parsed
while(<F>){
  # ... do work here   
}
close F;
# the results are availble here in some form

正則表達式可能是最好的解決方案,盡管我確實在CPAN中找到了您可以使用的以下模塊

  • Java :: JVM :: Classfile - 解析已編譯的類文件並返回有關它們的信息。 您必須先編譯文件才能使用它。

此外,請記住,使用正則表達式捕獲多行注釋的所有可能變體可能很棘手。

您還需要跳過引用的字符串(如果您不處理引用的字符串,則甚至無法正確跳過注釋)。

我可能會編寫一個相當簡單,高效且不完整的標記化器,與我在節點566467中編寫的標記器非常相似。

基於該代碼,我可能只是挖掘非注釋/非字符串塊尋找\\bimport\\b\\b\\Q$toFind\\E\\b匹配。 也許類似於:

if( m[
        \G
        (?:
            [^'"/]+
          | /(?![/*])
        )+
    ]xgc
) {
    my $code = substr( $_, $-[0], $+[0] - $-[0] );
    my $imported = 0;
    while( $code =~ /\b(import\s+)?\Q$package\E\b/g ) {
        if( $1 ) {
            ... # Found importing of package
            while( $code =~ /\b\Q$class\E\b/g ) {
                ... # Found mention of imported class
            }
            last;
        }
        ... # Found a package reference
    }
} elsif( m[ \G ' (?: [^'\\]+ | \\. )* ' ]xgc
    ||   m[ \G " (?: [^"\\]+ | \\. )* " ]xgc
) {
    # skip quoted strings
} elsif(  m[\G//.*]g­c  ) {
    # skip C++ comments

這真的只是Baz的直接grep(或/(foo.bar。|)Baz /如果你擔心some.other.Baz的誤報),但忽略評論,不是嗎?

如果是這樣的話,我會把一個狀態引擎組合起來跟蹤你是否在進行多行注釋。 所需的正則表達並不特別。 未經測試的代碼 ):

my $in_comment;
my %matches;
my $line_num = 0;
my $full_target = 'foo.bar.Baz';
my $short_target = (split /\./, $full_target)[-1];  # segment after last . (Baz)

while (my $line = <F>) {
    $line_num++;
    if ($in_comment) {
        next unless $line =~ m|\*/|;  # ignore line unless it ends the comment
        $line =~ s|.*\*/||;           # delete everything prior to end of comment
    } elsif ($line =~ m|/\*|) {
        if ($line =~ m|\*/|) {        # catch /* and */ on same line
            $line =~ s|/\*.*\*/||;
        } else {
            $in_comment = 1;
            $line =~ s|/\*.*||;       # clear from start of comment to end of line
        }
    }

    $line =~ s/\\\\.*//;   # remove single-line comments
    $matches{$line_num} = $line if $line =~ /$full_target| $short_target/;
}

for my $key (sort keys %matches) {
    print $key, ': ', $matches{$key}, "\n";
}

它並不完美,注釋狀態的輸入/輸出可能會被嵌套的多行注釋搞砸,或者如果同一行上有多個多行注釋,但這對於大多數真實案例來說可能已經足夠了。

要在沒有狀態引擎的情況下執行此操作,您需要插入單個字符串,刪除/ ... / comments,然后將其拆分為單獨的行,並為非//注釋命中grep。 但是你不能以這種方式在輸出中包含行號。

這就是我想出來的,它適用於我拋出的所有不同情況。 我仍然是一個Perl noob,它可能不是世界上最快的東西,但它應該適用於我需要的東西。 感謝所有答案,他們幫助我以不同的方式看待它。

  my $className = 'Baz';
  my $searchPkg = 'foo.bar';
  my @potentialRefs, my @confirmedRefs;
  my $samePkg = 0;
  my $imported = 0;
  my $currentPkg = 'com.bob';
  $currentPkg =~ s/\//\./g;
  if($currentPkg eq $searchPkg){
    $samePkg = 1;  
  }
  my $inMultiLineComment = 0;
  open F, $_ or die;
  my $lineNum = 0;
  while(<F>){
    $lineNum++;
    if($inMultiLineComment){
      if(m|^.*?\*/|){
        s|^.*?\*/||; #get rid of the closing part of the multiline comment we're in
        $inMultiLineComment = 0;
      }else{
        next;
      }
    }
    if(length($_) > 0){
      s|"([^"\\]*(\\.[^"\\]*)*)"||g; #remove strings first since java cannot have multiline string literals
      s|/\*.*?\*/||g;  #remove any multiline comments that start and end on the same line
      s|//.*$||;  #remove the // comments from what's left
      if (m|/\*.*$|){
        $inMultiLineComment = 1 ;#now if you have any occurence of /* then then at least some of the next line is in the multiline comment
        s|/\*.*$||g;
      }
    }else{
      next; #no sense continuing to process a blank string
    }

    if (/^\s*(import )?($searchPkg)?(.*)?\b$className\b/){
      if($imported || $samePkg){
        push(@confirmedRefs, $lineNum);
      }else {
        push(@potentialRefs, $lineNum);
      }
      if($1){
        $imported = 1;
      } elsif($2){
        push(@confirmedRefs, $lineNum);
      }
    }
  }
  close F;      
  if($imported){
    push(@confirmedRefs,@potentialRefs);
  }

  for (@confirmedRefs){
    print "$_\n";
  }

如果你有足夠的冒險精神,你可以看看Parse :: RecDescent

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM