/**
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
 */

package net.sourceforge.pmd.lang.java.rule.performance;

import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
import net.sourceforge.pmd.lang.java.rule.AbstractInefficientZeroCheck;
import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence;

/**
 * This rule finds code which inefficiently determines empty strings. This code
 *
 * <pre>
 *         if(str.trim().length()==0){....
 * </pre>
 *
 * <p>
 * is quite inefficient as trim() causes a new String to be created. Smarter
 * code to check for an empty string would be:
 * </p>
 *
 * <pre>
 * Character.isWhitespace(str.charAt(i));
 * </pre>
 *
 * @author acaplan
 */
public class InefficientEmptyStringCheckRule extends AbstractInefficientZeroCheck {

    @Override
    public boolean isTargetMethod(JavaNameOccurrence occ) {
        if (occ.getNameForWhichThisIsAQualifier() != null
                && occ.getNameForWhichThisIsAQualifier().getImage().indexOf("trim") != -1) {
            Node pExpression = occ.getLocation().jjtGetParent().jjtGetParent();
            if (pExpression.jjtGetNumChildren() > 2 && "length".equals(pExpression.jjtGetChild(2).getImage())) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean appliesToClassName(String name) {
        return "String".equals(name);
    }

    @Override
    public Object visit(ASTPrimaryExpression node, Object data) {

        if (node.jjtGetNumChildren() > 3) {
            // Check last suffix
            if (!"isEmpty".equals(node.jjtGetChild(node.jjtGetNumChildren() - 2).getImage())) {
                return data;
            }

            Node prevCall = node.jjtGetChild(node.jjtGetNumChildren() - 4);
            String target = prevCall.jjtGetNumChildren() > 0 ? prevCall.jjtGetChild(0).getImage() : prevCall.getImage();
            if (target != null && ("trim".equals(target) || target.endsWith(".trim"))) {
                addViolation(data, node);
            }
        }
        return data;
    }

}

