/*
 * fb-contrib - Auxiliary detectors for Java programs
 * Copyright (C) 2005-2016 Bhaskar Maddala
 * Copyright (C) 2005-2016 Dave Brosius
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package com.mebigfatguy.fbcontrib.detect;

import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.LocalVariableTable;

import com.mebigfatguy.fbcontrib.utils.BugType;

import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.BytecodeScanningDetector;
import edu.umd.cs.findbugs.OpcodeStack;

/**
 * Find usage of EqualsBuilder from Apache commons, where the code invoke
 * equals() on the constructed object rather than isEquals()
 *
 * <pre>
 * new EqualsBuilder().append(this.name, other.name).equals(other);
 * </pre>
 */
public class CommonsEqualsBuilderToEquals extends BytecodeScanningDetector {

    private static final String LANG_EQUALS_BUILDER = "Lorg/apache/commons/lang/builder/EqualsBuilder;";
    private static final String LANG3_EQUALS_BUILDER = "Lorg/apache/commons/lang3/builder/EqualsBuilder;";
    private final OpcodeStack stack;
    private final BugReporter bugReporter;

    /**
     * constructs a CEBE detector given the reporter to report bugs on.
     *
     * @param bugReporter
     *            the sync of bug reports
     */
    public CommonsEqualsBuilderToEquals(final BugReporter bugReporter) {
        stack = new OpcodeStack();
        this.bugReporter = bugReporter;
    }

    /**
     * implements the visitor to pass through constructors and static
     * initializers to the byte code scanning code. These methods are not
     * reported, but are used to build SourceLineAnnotations for fields, if
     * accessed.
     *
     * @param obj
     *            the context object of the currently parsed code attribute
     */
    @Override
    public void visitCode(Code obj) {
        stack.resetForMethodEntry(this);
        LocalVariableTable lvt = getMethod().getLocalVariableTable();
        if (lvt != null) {
            super.visitCode(obj);
        }
    }

    @Override
    public void sawOpcode(int seen) {
        if (seen == INVOKEVIRTUAL) {
            String methodName = getNameConstantOperand();
            if ("equals".equals(methodName) && "(Ljava/lang/Object;)Z".equals(getSigConstantOperand()) && (stack.getStackDepth() > 1)) {
                String calledClass = stack.getStackItem(1).getSignature();
                if (LANG3_EQUALS_BUILDER.equals(calledClass) || LANG_EQUALS_BUILDER.equals(calledClass)) {
                    bugReporter.reportBug(new BugInstance(this, BugType.CEBE_COMMONS_EQUALS_BUILDER_ISEQUALS.name(), HIGH_PRIORITY).addClass(this)
                            .addMethod(this).addSourceLine(this));
                }
            }
        }
        super.sawOpcode(seen);
        stack.sawOpcode(this, seen);
    }
}

@@ -1312,19 +1312,16 @@
 e invoke
-%0A *
  equals(
@@ -2231,23 +2231,16 @@
 d static
-%0A     *
  initial
@@ -2298,23 +2298,16 @@
  are not
-%0A     *
  reporte
@@ -2321,16 +2321,23 @@
 are used
+%0A     *
  to buil
@@ -2374,23 +2374,16 @@
 elds, if
-%0A     *
  accesse
@@ -2783,24 +2783,42 @@
 int seen) %7B%0A
+        try %7B%0A    
         if (
@@ -2838,24 +2838,28 @@
 EVIRTUAL) %7B%0A
+    
             
@@ -2912,24 +2912,28 @@
             
+ 
+   
 if (%22equals%22
@@ -3057,24 +3057,28 @@
             
+   
+ 
 String calle
@@ -3120,24 +3120,28 @@
 ignature();%0A
+    
             
@@ -3255,16 +3255,20 @@
         
+ 
+   
 bugRepor
@@ -3414,16 +3414,20 @@
         
+    
 .addMeth
@@ -3457,16 +3457,20 @@
 this));%0A
+    
         
@@ -3483,34 +3483,42 @@
   %7D%0A            
-%7D%0A
+    %7D%0A    
         %7D%0A      
@@ -3503,32 +3503,56 @@
 %7D%0A            %7D%0A
+        %7D finally %7B%0A    
         super.sa
@@ -3570,24 +3570,28 @@
 n);%0A        
+   
+ 
 stack.sawOpc
@@ -3603,20 +3603,30 @@
 his, seen);%0A
+        %7D%0A
     %7D%0A%7D%0A

put super.sawOpcode in a finally block