Remove System.out statements from For Loops only

Any way to remove them only from for loop for loop for loop in file is easy ...

Before

for( ... ) {
...
System.out.println("string");
...
System.out.println("string");
...
}

      

After :

for( ... ) {
...
... 
...
}

      

+3


source to share


4 answers


It's tricky: what is the closing parenthesis that for

-loop closes ? Either you analyze the entire code, or you use heuristics. In the solution below, I require the closing curly brace to be the same as specifying the keyword for

:

$ perl -nE'
    if( /^(\s*)for\b/ .. /^$ws\}/ ) {
      $ws = $1 // $ws;
      /^\s*System\.out\.println/ or print;
    } else { print }'

      

In this case, a trigger statement is used COND1 .. COND2

. script can be used as a simple filter

$ perl -nE'...' <source >processed

      

or with a backup function:

$ perl -i.bak -nE'...' source

      

(creates a file source.bak

as a backup).

Only checked for example input; does not repeat a reasonable set of tests.
This script passes the GLES test by Prateek Nina.

To run this script for all Java files in a directory, do

$ perl -i.bak -nE'...' *.java

      

Edit



On Windows systems, the separator must be changed to "

. Also, we must do all the pushing ourselves.

> perl -nE"if(/^(\s*)for\b/../^$ws\}/){$ws=$1//$ws;/^\s*System\.out\.println/ or print}else{print}BEGIN{@ARGV=$#ARGV?@ARGV:glob$ARGV[0]}" *.java

      

Edit 2

Below is the implementation of the shape counting algorithm specified in the comments. This solution also makes backups. Command line arguments will be interpreted as glob expressions.

#!/usr/bin/perl
use strict; use warnings;

clean($_) for map glob($_), @ARGV;

sub clean {
    local @ARGV = @_;
    local $^I = ".bak";
    my $depth = 0;
    while (<>) {
        $depth ||= /^\s*for\b/ ? "0 but true" : 0;
        my $delta = ( ()= /\{/g ) - ( ()= /\}/g );
        $depth += $delta if $depth && $delta;
        $depth = 0 if $depth < 0;
        print unless $depth && /^\s*System\.out\.println/;
    }
    return !!1;
}

      

This also makes no comments. This will only recognize System.out.println

-statements that start a new line.

Example of use: > perl thisScript.pl *.java

.

Here is a test file with pseudo-java syntax that I used for testing. All lines marked with a sign XXX

will disappear after the script is run.

/** Java test suite **/

bare block {
    System.out.println(...); // 1 -- let stand
}

if (true) {
    for (foo in bar) {
        System.out.println; // 2 XXX
        if (x == y) {
            // plz kill this
            System.out.println // 3 XXX
        } // don't exit here
        System.out.println // 4 XXX
    }
}

for (...) {
    for {
        // will this be removed?
        System.out.println // 5 XXX
    }
}

/* pathological cases */

// intendation
for (...) { System.out.println()/* 6 */} 

// intendation 2
for (...)
{
    if (x)
    {
        System.out.println // 7 XXX
    }}

// inline weirdness
for (...) {
    // "confuse" script here
    foo = new baz() {void qux () {...}
    };
    System.out.println // 8 XXX
}

      

No. 1 is to stay and do. Statement No. 6 should be deleted; but these scripts are unable to do this.

+6


source


I would suggest a two-fold approach, using the PMD static code analyzer to find the problem statements and a simple script to remove the lines. All sources and configuration are included below, EDIT , including Python and Groovy alternatives.

PMD has an extension mechanism to allow new rules to be added simply by using a simple XPath expression. In my implementation below, I am using:

        //WhileStatement/Statement/descendant-or-self::
            Statement[./StatementExpression/PrimaryExpression/PrimaryPrefix/Name[@Image="System.out.println"]]
        |
        //ForStatement/Statement/descendant-or-self::
            Statement[./StatementExpression/PrimaryExpression/PrimaryPrefix/Name[@Image="System.out.println"]]

      

The advantages of using this approach are as follows:

  • No regular expressions
  • A graphical editor for developing and refining the rules - you can fine tune the rules that I have given to deal with any other scripts you have
  • Handles all the strange formatting of the Java source - PMD uses the JavaCC compiler to understand the structure of all valid Java files.
  • Processes where it System.out.println

    is within the conditional loop - to any depth
  • Processes where instructions are split across multiple lines.
  • Can be used in Eclipse and IntelliJ

Instructions

  • Create a PMD rule in a custom ruleset.

    • Create a directory rulesets/java

      somewhere on CLASSPATH

      - it can be under the working directory if "." is on the way.
    • In this directory, create a ruleset XML file named custom.xml

      , containing:

      <?xml version="1.0"?>
      <ruleset name="Custom"
          xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
      
          <description>Detecting System.out.println's</description>
          <rule name="LoopedSystemOutPrintlns"
                message="System.out.println() statements in a for or while loop"
                language="java"
                class="net.sourceforge.pmd.lang.rule.XPathRule">
            <description>
               Find System.out.println() statements in for or while loops.
            </description>
            <priority>1</priority>
            <properties>
              <property name="xpath">
              <value>
                <![CDATA[
      //WhileStatement/Statement/descendant-or-self::
          Statement[./StatementExpression/PrimaryExpression/PrimaryPrefix/Name[@Image="System.out.println"]]
      |
      //ForStatement/Statement/descendant-or-self::
          Statement[./StatementExpression/PrimaryExpression/PrimaryPrefix/Name[@Image="System.out.println"]]
                ]]>
                </value>
              </property>
            </properties>
          </rule>
      </ruleset>
      
            

    • Create a rulesets.properties file containing the following line:

      rulesets.filenames=rulesets/java/custom.xml
      
            

    • Fine! PMD is now set up with your new rule that identifies all cases System.out.println

      within any loop anywhere in your code. Your ruleset is now called "java-custom" because it is "custom.xml" in the "java" directory

  • Run PMD on your codebase, choosing only your ruleset, java-custom. Use an XML report to get both the start and end lines. Capture the result in the "errors.xml" file:

    $ pmd -d <SOURCEDIR> -f xml -r java-custom > violations.xml
    
          

    Creates a file similar to:

    <?xml version="1.0" encoding="UTF-8"?>
    <pmd version="5.0.1" timestamp="2013-01-28T11:22:25.688">
    <file name="SOURCEDIR/Example.java">
    <violation beginline="7" endline="11" begincolumn="13" endcolumn="39" rule="LoopedSystemOutPrintlns" ruleset="Custom" class="Example" method="bar" priority="1">
    System.out.println() statements in a for or while loop
    </violation>
    <violation beginline="15" endline="15" begincolumn="13" endcolumn="38" rule="LoopedSystemOutPrintlns" ruleset="Custom" class="Example" method="bar" priority="1">
    System.out.println() statements in a for or while loop
    </violation>
    <violation beginline="18" endline="18" begincolumn="13" endcolumn="38" rule="LoopedSystemOutPrintlns" ruleset="Custom" class="Example" method="bar" priority="1">
    System.out.println() statements in a for or while loop
    </violation>
    <violation beginline="20" endline="21" begincolumn="17" endcolumn="39" rule="LoopedSystemOutPrintlns" ruleset="Custom" class="Example" method="bar" priority="1">
    System.out.println() statements in a for or while loop
    </violation>
    </file>
    </pmd>
    
          

    You can use this report to verify that the PMD has identified the correct statements.

  • Create a Python script (NOTE: Groovy alternative is listed at the bottom of the answer) to read the violations XML file and process the original files

    • Create a file named remover.py

      in a directory in the classpath
    • Add the following Python to it:

      from xml.etree.ElementTree import ElementTree
      from os import rename, path
      from sys import argv
      
      def clean_file(source, target, violations):
          """Read file from source outputting all lines, *except* those in the set
          violations, to the file target"""
          infile  = open(source, 'r' )
          outfile = open(target, "w")
          for num, line in enumerate(infile.readlines(), start=1):
              if num not in violations:
                  outfile.write(line)
          infile.close()
          outfile.close()
      
      
      def clean_all(pmd_xml):
          """Read a PMD violations XML file; for each file identified, remove all 
          lines that are marked as violations"""
          tree = ElementTree()
          tree.parse(pmd_xml)
          for file in tree.findall("file"):
              # Create a list of lists. Each inner list identifies all the lines
              # in a single violation.
              violations = [ range(int(violation.attrib['beginline']), int(violation.attrib['endline'])+1) for violation in file.findall("violation")]
              # Flatten the list of lists into a set of line numbers
              violations = set( i for j in violations for i in j )
      
              if violations:
                  name = file.attrib['name']
                  bak  = name + ".bak"
                  rename(name, bak)
                  clean_file(bak, name, violations)
      
      if __name__ == "__main__":
          if len(argv) != 2 or not path.exists(argv[1]):
              exit(argv[0] + " <PMD violations XML file>")
          clean_all(argv[1])
      
            

  • Run Python script. This will rename the corresponding files by adding ".bak" and then rewrite the Java file without the offensive lines. This can be devastating, so make sure your files were copied correctly first . In particular, don't run the script twice in a row - the second timeout will naively remove the same line numbers, even if they've already been removed:

    $ python remover.py violations.xml
    
          




EDIT

For those who prefer a more Java oriented script to remove statements System.out.println

from violations.xml

, I present the following Groovy:

    def clean_file(source, target, violations) {
        new File(target).withWriter { out ->
            new File(source).withReader { reader ->
                def i = 0
                while (true) {
                    def line = reader.readLine()
                    if (line == null) {
                        break
                    }  else {
                        i++
                        if(!(i in violations)) {
                            out.println(line)
                        }
                    }
                }
            }
        }
    }

    def linesToRemove(file_element) {
        Set lines = new TreeSet()
        for (violation in file_element.children()) {
            def i = Integer.parseInt(violation.@beginline.text())
            def j = Integer.parseInt(violation.@endline.text())
            lines.addAll(i..j)
        }
        return lines
    }

    def clean_all(file_name) {
        def tree = new XmlSlurper().parse(file_name)
        for (file in tree.children()) {
            def violations = linesToRemove(file)
            if (violations.size() > 0) {
                def origin = file.@name.text()
                def backup = origin + ".bak"
                new File(origin).renameTo(new File(backup))
                clean_file(backup, origin, violations)
            }
        }
    }

    clean_all("violations.xml")

      


As a general note, calls are System.out.println

not necessarily an issue - perhaps your assertions are shaped "Calling method on " + obj1 + " with param " + obj2 + " -> " + (obj1.myMethod(obj2))

, and the real cost is both string concatenation (better with StringBuffer / StringBuilder) and method call cost.

+4


source


Edit:

1.nested for

hinges fixed

2.files .java

are now selected recursively

Note:

When you are sure of the code, replace line 45: open( hanw , "+>".$file.".txt" );

with this line: open( hanw , "+>".$file );

application.pl

use strict;
use File::Find qw( finddepth );
our $root = "src/";
our $file_data = {};
our @java_files;

finddepth( sub {
  if( $_ eq '.' || $_ eq '..' ) {
    return;
  } else {
    if( /\.java$/i ) {
      push( @java_files , $File::Find::name );
    }
  }
} , $root );

sub clean {
  my $file = shift;
  open( hanr , $file );
  my @input_lines = <hanr>;
  my $inside_for = 0;

  foreach( @input_lines ) {
    if( $_ =~ /(\s){0,}for(\s){0,}\((.*)\)(\s){0,}\{(\s){0,}/ ) {
      $inside_for++;
      push( @{$file_data->{$file}} , $_ );
    } elsif( $inside_for > 0 ) {
        if( $_ =~ /(\s){0,}System\.out\.println\(.*/ ) {
        } elsif( $_ =~ /(\s){0,}\}(\s){0,}/ ) {
          $inside_for--;
          push( @{$file_data->{$file}} , $_ );
        } else {
          push( @{$file_data->{$file}} , $_ );
        }
    } else {
      push( @{$file_data->{$file}} , $_ );
    }
  }
}

foreach ( @java_files ) {
  $file_data->{$_} = [];
  clean( $_ );
}

foreach my $file ( keys %$file_data ) {
  open( hanw , "+>".$file.".txt" );
  foreach( @{$file_data->{$file}} ) {
    print hanw $_;
  }
}

      

data1.java

class Employee {
  /* code */
  public void Employee() {
    System.out.println("string");
    for( ... ) {
      System.out.println("string");
      /* code */
      System.out.println("string");
      for( ... ) {
        System.out.println("string");
        /* code */
        System.out.println("string");
      }
    }
  }
}

for( ... ) {
  System.out.println("string");
  /* code */
  System.out.println("string");
}

      

data2.java

for( ... ) {
  /* code */
  System.out.println("string");
  /* code */
  System.out.println("string");
  /* code */
  for( ... ) {
    System.out.println("string");
    /* code */
    System.out.println("string");
    for( ... ) {
      System.out.println("string");
      /* code */
      System.out.println("string");
    }
  }
}

public void display() {
  /* code */
  System.out.println("string");
  for( ... ) {
    System.out.println("string");
    /* code */
    System.out.println("string");
    for( ... ) {
      System.out.println("string");
      /* code */
      System.out.println("string");
    }
  }
}

      

data1.java.txt

class Employee {
  /* code */
  public void Employee() {
    System.out.println("string");
    for( ... ) {
      /* code */
      for( ... ) {
        /* code */
      }
    }
  }
}

for( ... ) {
  /* code */
}

      

data2.java.txt

for( ... ) {
  /* code */
  /* code */
  /* code */
  for( ... ) {
    /* code */
    for( ... ) {
      /* code */
    }
  }
}

public void display() {
  /* code */
  System.out.println("string");
  for( ... ) {
    /* code */
    for( ... ) {
      /* code */
    }
  }
}

      

image-1

+2


source


So you are looking for Java parsing capability. A quick Google search reveals javaparser , a Java 1.5 parser written in Java.

+1


source







All Articles