Search code examples
javacommentscode-analysisinria-spoon

How to remove Comments from a java Class using Spoon?


I am using SequenceR to parse my Java code files. It uses Spoon library of Java which is a popular code analysis library. What I need is, I will give a buggy line number to the analyzer, it will detect the line and find out the method or class of it and it will remove all the comments on that class or method.

I have tried in several ways to achieve that. But can't really get the desired output.

This is my input Java file.


public class CWE23_Relative_Path_Traversal__connect_tcp_01 extends AbstractTestCase
{
    /* uses badsource and badsink */
    public void bad() throws Throwable
    {
        String data;

        data = ""; /* Initialize data */

        /* Read data using an outbound tcp connection */
        {
            Socket socket = null;
            BufferedReader readerBuffered = null;
            InputStreamReader readerInputStream = null;

            try
            {
                /* Read data using an outbound tcp connection */
                socket = new Socket("host.example.org", 39544);

                /* read input from socket */

                readerInputStream = new InputStreamReader(socket.getInputStream(), "UTF-8");
                readerBuffered = new BufferedReader(readerInputStream);

                /* POTENTIAL FLAW: Read data using an outbound tcp connection */
                data = readerBuffered.readLine();
            }
            catch (IOException exceptIO)
            {
                IO.logger.log(Level.WARNING, "Error with stream reading", exceptIO);
            }
            finally
            {
                /* clean up stream reading objects */
                try
                {
                    if (readerBuffered != null)
                    {
                        readerBuffered.close();
                    }
                }
                catch (IOException exceptIO)
                {
                    IO.logger.log(Level.WARNING, "Error closing BufferedReader", exceptIO);
                }

                try
                {
                    if (readerInputStream != null)
                    {
                        readerInputStream.close();
                    }
                }
                catch (IOException exceptIO)
                {
                    IO.logger.log(Level.WARNING, "Error closing InputStreamReader", exceptIO);
                }

                /* clean up socket objects */
                try
                {
                    if (socket != null)
                    {
                        socket.close();
                    }
                }
                catch (IOException exceptIO)
                {
                    IO.logger.log(Level.WARNING, "Error closing Socket", exceptIO);
                }
            }
        }

        String root;
        if(System.getProperty("os.name").toLowerCase().indexOf("win") >= 0)
        {
            /* running on Windows */
            root = "C:\\uploads\\";
        }
        else
        {
            /* running on non-Windows */
            root = "/home/user/uploads/";
        }

        if (data != null)
        {
            /* POTENTIAL FLAW: no validation of concatenated value */
            File file = new File(root + data);
            FileInputStream streamFileInputSink = null;
            InputStreamReader readerInputStreamSink = null;
            BufferedReader readerBufferdSink = null;
            if (file.exists() && file.isFile())
            {
                try
                {
                    streamFileInputSink = new FileInputStream(file);
                    readerInputStreamSink = new InputStreamReader(streamFileInputSink, "UTF-8");
                    readerBufferdSink = new BufferedReader(readerInputStreamSink);
                    IO.writeLine(readerBufferdSink.readLine());
                }
                catch (IOException exceptIO)
                {
                    IO.logger.log(Level.WARNING, "Error with stream reading", exceptIO);
                }
                finally
                {
                    /* Close stream reading objects */
                    try
                    {
                        if (readerBufferdSink != null)
                        {
                            readerBufferdSink.close();
                        }
                    }
                    catch (IOException exceptIO)
                    {
                        IO.logger.log(Level.WARNING, "Error closing BufferedReader", exceptIO);
                    }

                    try
                    {
                        if (readerInputStreamSink != null)
                        {
                            readerInputStreamSink.close();
                        }
                    }
                    catch (IOException exceptIO)
                    {
                        IO.logger.log(Level.WARNING, "Error closing InputStreamReader", exceptIO);
                    }

                    try
                    {
                        if (streamFileInputSink != null)
                        {
                            streamFileInputSink.close();
                        }
                    }
                    catch (IOException exceptIO)
                    {
                        IO.logger.log(Level.WARNING, "Error closing FileInputStream", exceptIO);
                    }
                }
            }
        }

    }

    public void good() throws Throwable
    {
        goodG2B();
    }

    /* goodG2B() - uses goodsource and badsink */
    private void goodG2B() throws Throwable
    {
        String data;

        /* FIX: Use a hardcoded string */
        data = "foo";

        String root;
        if(System.getProperty("os.name").toLowerCase().indexOf("win") >= 0)
        {
            /* running on Windows */
            root = "C:\\uploads\\";
        }
        else
        {
            /* running on non-Windows */
            root = "/home/user/uploads/";
        }

        if (data != null)
        {
            /* POTENTIAL FLAW: no validation of concatenated value */
            File file = new File(root + data);
            FileInputStream streamFileInputSink = null;
            InputStreamReader readerInputStreamSink = null;
            BufferedReader readerBufferdSink = null;
            if (file.exists() && file.isFile())
            {
                try
                {
                    streamFileInputSink = new FileInputStream(file);
                    readerInputStreamSink = new InputStreamReader(streamFileInputSink, "UTF-8");
                    readerBufferdSink = new BufferedReader(readerInputStreamSink);
                    IO.writeLine(readerBufferdSink.readLine());
                }
                catch (IOException exceptIO)
                {
                    IO.logger.log(Level.WARNING, "Error with stream reading", exceptIO);
                }
                finally
                {
                    /* Close stream reading objects */
                    try
                    {
                        if (readerBufferdSink != null)
                        {
                            readerBufferdSink.close();
                        }
                    }
                    catch (IOException exceptIO)
                    {
                        IO.logger.log(Level.WARNING, "Error closing BufferedReader", exceptIO);
                    }

                    try
                    {
                        if (readerInputStreamSink != null)
                        {
                            readerInputStreamSink.close();
                        }
                    }
                    catch (IOException exceptIO)
                    {
                        IO.logger.log(Level.WARNING, "Error closing InputStreamReader", exceptIO);
                    }

                    try
                    {
                        if (streamFileInputSink != null)
                        {
                            streamFileInputSink.close();
                        }
                    }
                    catch (IOException exceptIO)
                    {
                        IO.logger.log(Level.WARNING, "Error closing FileInputStream", exceptIO);
                    }
                }
            }
        }

    }

    /* Below is the main(). It is only used when building this testcase on
     * its own for testing or for building a binary to use in testing binary
     * analysis tools. It is not used when compiling all the testcases as one
     * application, which is how source code analysis tools are tested.
     */
    public static void main(String[] args) throws ClassNotFoundException,
           InstantiationException, IllegalAccessException
    {
        mainFromParent(args);
    }
}

What I have done in my analyzer code is this -


    public static void generateAbstraction(File buggy_file, int buggy_line, File working_dir) {
        Launcher launcher = new Launcher();
        launcher.getEnvironment().setAutoImports(true);
        launcher.getEnvironment().setNoClasspath(true);
        launcher.getEnvironment().setCommentEnabled(true);
        launcher.addInputResource(buggy_file.toString());
        try {
            launcher.buildModel();
        } catch (Exception e) {

        }

        CtModel model = launcher.getModel();

        CtMethod topLevelmethod = null;
        CtElement buggy_ctElement = null;
        CtElement tmp_ctElement = null;
        CtPath buggy_ctElement_ctPath = null;


        // This is the main part

        for (CtType<?> ctType : model.getAllTypes()) {

            for (Iterator<CtElement> desIter = ctType.descendantIterator(); desIter.hasNext(); ) {
                tmp_ctElement = desIter.next();

                try {

                    // Main problem resides here

                    if (tmp_ctElement.getPosition().getLine() == buggy_line && !(tmp_ctElement instanceof CtComment)) {
                        buggy_ctElement = tmp_ctElement;
                        buggy_ctElement_ctPath = tmp_ctElement.getPath();

                        topLevelmethod = getTopLevelMethod(buggy_ctElement);

                        List<CtComment> comments = topLevelmethod.getElements(
                                new TypeFilter<CtComment>(CtComment.class)
                        );

                        for(CtComment c: comments){
                               topLevelmethod.removeComment(c);
                        }

                        break;
                    }
                } catch (java.lang.UnsupportedOperationException e) {
                    continue;
                }
            }

            // ......
        }

    }

    public static CtMethod getTopLevelMethod(CtElement ctElement) {
        CtMethod topLevelMethod = null;
        topLevelMethod = ctElement.getParent(CtMethod.class);
        while (topLevelMethod != null && topLevelMethod.getParent(CtMethod.class) != null) {
            System.out.println();
            topLevelMethod = topLevelMethod.getParent(CtMethod.class);
        }
        return topLevelMethod;
    }

The output goes like this -

public class CWE23_Relative_Path_Traversal__connect_tcp_01 extends AbstractTestCase {
    public void bad() throws Throwable {
        String data;
        data = "";/* Initialize data */

        /* Read data using an outbound tcp connection */
        {
            Socket socket = null;
            BufferedReader readerBuffered = null;
            InputStreamReader readerInputStream = null;
            try {
                /* Read data using an outbound tcp connection */
                socket = new Socket("host.example.org", 39544);
                /* read input from socket */
                readerInputStream = new InputStreamReader(socket.getInputStream(), "UTF-8");
                readerBuffered = new BufferedReader(readerInputStream);
                /* POTENTIAL FLAW: Read data using an outbound tcp connection */
                data = readerBuffered.readLine();
            } catch (IOException exceptIO) {
                logger.log(Level.WARNING, "Error with stream reading", exceptIO);
            } finally {
                /* clean up stream reading objects */
                try {
                    if (readerBuffered != null) {
                        readerBuffered.close();
                    }
                } catch (IOException exceptIO) {
                    logger.log(Level.WARNING, "Error closing BufferedReader", exceptIO);
                }
                try {
                    if (readerInputStream != null) {
                        readerInputStream.close();
                    }
                } catch (IOException exceptIO) {
                    logger.log(Level.WARNING, "Error closing InputStreamReader", exceptIO);
                }
                /* clean up socket objects */
                try {
                    if (socket != null) {
                        socket.close();
                    }
                } catch (IOException exceptIO) {
                    logger.log(Level.WARNING, "Error closing Socket", exceptIO);
                }
            }
        }
        String root;
        if ((System.getProperty("os.name").toLowerCase().indexOf("win")) >= 0) {
            /* running on Windows */
            root = "C:\\uploads\\";
        }else {
            /* running on non-Windows */
            root = "/home/user/uploads/";
        }
        if (data != null) {
            /* POTENTIAL FLAW: no validation of concatenated value */
            File file = new File((root + data));
            FileInputStream streamFileInputSink = null;
            InputStreamReader readerInputStreamSink = null;
            BufferedReader readerBufferdSink = null;
            if ((file.exists()) && (file.isFile())) {
                try {
                    streamFileInputSink = new FileInputStream(file);
                    readerInputStreamSink = new InputStreamReader(streamFileInputSink, "UTF-8");
                    readerBufferdSink = new BufferedReader(readerInputStreamSink);
                    IO.writeLine(readerBufferdSink.readLine());
                } catch (IOException exceptIO) {
                    logger.log(Level.WARNING, "Error with stream reading", exceptIO);
                } finally {
                    /* Close stream reading objects */
                    try {
                        if (readerBufferdSink != null) {
                            readerBufferdSink.close();
                        }
                    } catch (IOException exceptIO) {
                        logger.log(Level.WARNING, "Error closing BufferedReader", exceptIO);
                    }
                    try {
                        if (readerInputStreamSink != null) {
                            readerInputStreamSink.close();
                        }
                    } catch (IOException exceptIO) {
                        logger.log(Level.WARNING, "Error closing InputStreamReader", exceptIO);
                    }
                    try {
                        if (streamFileInputSink != null) {
                            streamFileInputSink.close();
                        }
                    } catch (IOException exceptIO) {
                        logger.log(Level.WARNING, "Error closing FileInputStream", exceptIO);
                    }
                }
            }
        }
    }

    public void good() throws Throwable {
    }

    /* goodG2B() - uses goodsource and badsink */
    private void goodG2B() throws Throwable {
    }

    /* Below is the main(). It is only used when building this testcase on
    its own for testing or for building a binary to use in testing binary
    analysis tools. It is not used when compiling all the testcases as one
    application, which is how source code analysis tools are tested.
     */
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
    }
}

If you look at it closely, you can see that only one comment was removed. That is -

/* uses badsource and badsink */

Other comments are intact. But I need to remove all the comments in a class or method. What am I doing wrong? I am totally new to spoon. Any help would be appreciated.


Solution

  • I accidentally found the answer a few minutes ago. I am not gonna delete this answer because someone might find this useful. To remove all the comments of the file, you just need to turn a flag off.

    This line did the magic -

    launcher.getEnvironment().setCommentEnabled(false);
    

    What it does is, it sets the setCommentEnabled value false and comments are ignored totally. So, you don't need to worry about them at all. This simple solution took me 6 hours to figure out.