class LocalVariableCleaner extends DepthFirstAdapter {
  public final boolean DEBUG = false;

  ASTNode AST;

  ASTUsesAndDefs useDefs;

  ASTParentNodeFinder parentOf;

  public LocalVariableCleaner(ASTNode AST) {
    super();
    this.AST = AST;
    parentOf = new ASTParentNodeFinder();
    AST.apply(parentOf);
  }

  public LocalVariableCleaner(boolean verbose, ASTNode AST) {
    super(verbose);
    this.AST = AST;
    parentOf = new ASTParentNodeFinder();
    AST.apply(parentOf);
  }

  /*
   * Get all locals declared in the method If the local is never defined (and hence never used) remove it If the local is
   * defined BUT never used then you may remove it IF AND ONLY IF The definition is either a copy stmt or an assignment of a
   * constant (i.e. no side effects)
   */
  public void outASTMethodNode(ASTMethodNode node) {
    boolean redo = false;

    useDefs = new ASTUsesAndDefs(AST); // create the uD and dU chains
    AST.apply(useDefs);

    // get all local variables declared in this method
    Iterator decIt = node.getDeclaredLocals().iterator();

    ArrayList<Local> removeList = new ArrayList<Local>();
    while (decIt.hasNext()) {
      // going through each local declared

      Local var = (Local) decIt.next();

      List<DefinitionStmt> defs = getDefs(var);

      // if defs is 0 it means var never got defined
      if (defs.size() == 0) {
        // var is never defined and hence is certainly not used anywhere
        removeList.add(var);
      } else {
        // if a var is defined but not used then in some conditions we can remove it

        // check that each def is removable
        Iterator<DefinitionStmt> defIt = defs.iterator();

        while (defIt.hasNext()) {
          DefinitionStmt ds = defIt.next();

          if (canRemoveDef(ds)) {
            // if removeStmt is successful since something change we need to redo
            // everything hoping something else might be removed....
            // in this case method returns true
            redo = removeStmt(ds);
          }
        } // while going through defs

      } // end else defs was not zero
    } // going through each stmt

    // go through the removeList and remove all locals
    Iterator<Local> remIt = removeList.iterator();
    while (remIt.hasNext()) {
      Local removeLocal = remIt.next();
      node.removeDeclaredLocal(removeLocal);

      /*
       * Nomair A. Naeem 7th Feb 2005 these have to be removed from the legacy lists in DavaBody also
       *
       */
      // retrieve DavaBody
      if (AST instanceof ASTMethodNode) {
        // this should always be true but whatever
        DavaBody body = ((ASTMethodNode) AST).getDavaBody();
        if (DEBUG) {
          System.out.println("body information");
          System.out.println("Control local is: " + body.get_ControlLocal());
          System.out.println("his locals are: " + body.get_ThisLocals());
          System.out.println("Param Map is: " + body.get_ParamMap());
          System.out.println("Locals are:" + body.getLocals());
        }
        Collection<Local> localChain = body.getLocals();
        if (removeLocal != null && localChain != null) {
          localChain.remove(removeLocal);
        }
      } else {
        throw new DecompilationException("found AST which is not a methodNode");
      }

      if (DEBUG) {
        System.out.println("Removed" + removeLocal);
      }
      redo = true;
    }

    if (redo) {
      // redo the whole function
      outASTMethodNode(node);
    }
  }

  /*
   * A def can be removed if and only if: 1, there are no uses of this definition 2, the right hand size is either a local or
   * a constant i.e. no need to worry about side effects
   */
  public boolean canRemoveDef(DefinitionStmt ds) {
    List uses = useDefs.getDUChain(ds);

    if (uses.size() != 0) {
      return false;
    }

    // there is no use of this def, we can remove it if it is copy stmt or a constant assignment
    if (ds.getRightOp() instanceof Local || ds.getRightOp() instanceof Constant) {
      return true;
    }

    return false;
  }

  /*
   * This method looks up all defs and returns those of this local
   */
  public List<DefinitionStmt> getDefs(Local var) {
    List<DefinitionStmt> toReturn = new ArrayList<DefinitionStmt>();

    HashMap<Object, List> dU = useDefs.getDUHashMap();
    Iterator<Object> it = dU.keySet().iterator();
    while (it.hasNext()) {
      DefinitionStmt s = (DefinitionStmt) it.next();
      Value left = s.getLeftOp();
      if (left instanceof Local) {
        if (((Local) left).getName().compareTo(var.getName()) == 0) {
          toReturn.add(s);
        }
      }
    }
    return toReturn;
  }

  public boolean removeStmt(Stmt stmt) {<FILL_FUNCTION_BODY>}

}

    Object tempParent = parentOf.getParentOf(stmt);
    if (tempParent == null) {
      // System.out.println("NO PARENT FOUND CANT DO ANYTHING");
      return false;
    }

    // parents are always ASTNodes, hence safe to cast
    ASTNode parent = (ASTNode) tempParent;

    // REMOVING STMT
    if (!(parent instanceof ASTStatementSequenceNode)) {
      // parent of a statement should always be a ASTStatementSequenceNode
      return false;
    }
    ASTStatementSequenceNode parentNode = (ASTStatementSequenceNode) parent;

    ArrayList<AugmentedStmt> newSequence = new ArrayList<AugmentedStmt>();
    int size = parentNode.getStatements().size();
    for (AugmentedStmt as : parentNode.getStatements()) {
      Stmt s = as.get_Stmt();
      if (s.toString().compareTo(stmt.toString()) != 0) {
        // this is not the stmt to be removed
        newSequence.add(as);
      }
    }
    // System.out.println("STMT REMOVED---------------->"+stmt);
    parentNode.setStatements(newSequence);
    if (newSequence.size() < size) {
      return true; // size of new node is smaller than orignal size
    }

    return false;// didnt actually delete anything for some weird reason...shouldnt happen
  
<methods>public void <init>() ,public void <init>(boolean) ,public void caseASTAndCondition(soot.dava.internal.AST.ASTAndCondition) ,public void caseASTBinaryCondition(soot.dava.internal.AST.ASTBinaryCondition) ,public void caseASTDoWhileNode(soot.dava.internal.AST.ASTDoWhileNode) ,public void caseASTForLoopNode(soot.dava.internal.AST.ASTForLoopNode) ,public void caseASTIfElseNode(soot.dava.internal.AST.ASTIfElseNode) ,public void caseASTIfNode(soot.dava.internal.AST.ASTIfNode) ,public void caseASTLabeledBlockNode(soot.dava.internal.AST.ASTLabeledBlockNode) ,public void caseASTMethodNode(soot.dava.internal.AST.ASTMethodNode) ,public void caseASTOrCondition(soot.dava.internal.AST.ASTOrCondition) ,public void caseASTStatementSequenceNode(soot.dava.internal.AST.ASTStatementSequenceNode) ,public void caseASTSwitchNode(soot.dava.internal.AST.ASTSwitchNode) ,public void caseASTSynchronizedBlockNode(soot.dava.internal.AST.ASTSynchronizedBlockNode) ,public void caseASTTryNode(soot.dava.internal.AST.ASTTryNode) ,public void caseASTUnaryCondition(soot.dava.internal.AST.ASTUnaryCondition) ,public void caseASTUnconditionalLoopNode(soot.dava.internal.AST.ASTUnconditionalLoopNode) ,public void caseASTWhileNode(soot.dava.internal.AST.ASTWhileNode) ,public void caseArrayRef(soot.jimple.ArrayRef) ,public void caseBinopExpr(soot.jimple.BinopExpr) ,public void caseCastExpr(soot.jimple.CastExpr) ,public void caseDVariableDeclarationStmt(soot.dava.internal.javaRep.DVariableDeclarationStmt) ,public void caseDefinitionStmt(soot.jimple.DefinitionStmt) ,public void caseExpr(soot.jimple.Expr) ,public void caseExprOrRefValueBox(soot.ValueBox) ,public void caseInstanceFieldRef(soot.jimple.InstanceFieldRef) ,public void caseInstanceInvokeExpr(soot.jimple.InstanceInvokeExpr) ,public void caseInstanceOfExpr(soot.jimple.InstanceOfExpr) ,public void caseInvokeExpr(soot.jimple.InvokeExpr) ,public void caseInvokeStmt(soot.jimple.InvokeStmt) ,public void caseNewArrayExpr(soot.jimple.NewArrayExpr) ,public void caseNewMultiArrayExpr(soot.jimple.NewMultiArrayExpr) ,public void caseRef(soot.jimple.Ref) ,public void caseReturnStmt(soot.jimple.ReturnStmt) ,public void caseStaticFieldRef(soot.jimple.StaticFieldRef) ,public void caseStmt(soot.jimple.Stmt) ,public void caseThrowStmt(soot.jimple.ThrowStmt) ,public void caseType(soot.Type) ,public void caseUnopExpr(soot.jimple.UnopExpr) ,public void caseValue(soot.Value) ,public void debug(java.lang.String, java.lang.String, java.lang.String) ,public void decideCaseExpr(soot.jimple.Expr) ,public void decideCaseExprOrRef(soot.Value) ,public void decideCaseRef(soot.jimple.Ref) ,public void inASTAndCondition(soot.dava.internal.AST.ASTAndCondition) ,public void inASTBinaryCondition(soot.dava.internal.AST.ASTBinaryCondition) ,public void inASTDoWhileNode(soot.dava.internal.AST.ASTDoWhileNode) ,public void inASTForLoopNode(soot.dava.internal.AST.ASTForLoopNode) ,public void inASTIfElseNode(soot.dava.internal.AST.ASTIfElseNode) ,public void inASTIfNode(soot.dava.internal.AST.ASTIfNode) ,public void inASTLabeledBlockNode(soot.dava.internal.AST.ASTLabeledBlockNode) ,public void inASTMethodNode(soot.dava.internal.AST.ASTMethodNode) ,public void inASTOrCondition(soot.dava.internal.AST.ASTOrCondition) ,public void inASTStatementSequenceNode(soot.dava.internal.AST.ASTStatementSequenceNode) ,public void inASTSwitchNode(soot.dava.internal.AST.ASTSwitchNode) ,public void inASTSynchronizedBlockNode(soot.dava.internal.AST.ASTSynchronizedBlockNode) ,public void inASTTryNode(soot.dava.internal.AST.ASTTryNode) ,public void inASTUnaryCondition(soot.dava.internal.AST.ASTUnaryCondition) ,public void inASTUnconditionalLoopNode(soot.dava.internal.AST.ASTUnconditionalLoopNode) ,public void inASTWhileNode(soot.dava.internal.AST.ASTWhileNode) ,public void inArrayRef(soot.jimple.ArrayRef) ,public void inBinopExpr(soot.jimple.BinopExpr) ,public void inCastExpr(soot.jimple.CastExpr) ,public void inDVariableDeclarationStmt(soot.dava.internal.javaRep.DVariableDeclarationStmt) ,public void inDefinitionStmt(soot.jimple.DefinitionStmt) ,public void inExpr(soot.jimple.Expr) ,public void inExprOrRefValueBox(soot.ValueBox) ,public void inInstanceFieldRef(soot.jimple.InstanceFieldRef) ,public void inInstanceInvokeExpr(soot.jimple.InstanceInvokeExpr) ,public void inInstanceOfExpr(soot.jimple.InstanceOfExpr) ,public void inInvokeExpr(soot.jimple.InvokeExpr) ,public void inInvokeStmt(soot.jimple.InvokeStmt) ,public void inNewArrayExpr(soot.jimple.NewArrayExpr) ,public void inNewMultiArrayExpr(soot.jimple.NewMultiArrayExpr) ,public void inRef(soot.jimple.Ref) ,public void inReturnStmt(soot.jimple.ReturnStmt) ,public void inStaticFieldRef(soot.jimple.StaticFieldRef) ,public void inStmt(soot.jimple.Stmt) ,public void inThrowStmt(soot.jimple.ThrowStmt) ,public void inType(soot.Type) ,public void inUnopExpr(soot.jimple.UnopExpr) ,public void inValue(soot.Value) ,public void normalRetrieving(soot.dava.internal.AST.ASTNode) ,public void outASTAndCondition(soot.dava.internal.AST.ASTAndCondition) ,public void outASTBinaryCondition(soot.dava.internal.AST.ASTBinaryCondition) ,public void outASTDoWhileNode(soot.dava.internal.AST.ASTDoWhileNode) ,public void outASTForLoopNode(soot.dava.internal.AST.ASTForLoopNode) ,public void outASTIfElseNode(soot.dava.internal.AST.ASTIfElseNode) ,public void outASTIfNode(soot.dava.internal.AST.ASTIfNode) ,public void outASTLabeledBlockNode(soot.dava.internal.AST.ASTLabeledBlockNode) ,public void outASTMethodNode(soot.dava.internal.AST.ASTMethodNode) ,public void outASTOrCondition(soot.dava.internal.AST.ASTOrCondition) ,public void outASTStatementSequenceNode(soot.dava.internal.AST.ASTStatementSequenceNode) ,public void outASTSwitchNode(soot.dava.internal.AST.ASTSwitchNode) ,public void outASTSynchronizedBlockNode(soot.dava.internal.AST.ASTSynchronizedBlockNode) ,public void outASTTryNode(soot.dava.internal.AST.ASTTryNode) ,public void outASTUnaryCondition(soot.dava.internal.AST.ASTUnaryCondition) ,public void outASTUnconditionalLoopNode(soot.dava.internal.AST.ASTUnconditionalLoopNode) ,public void outASTWhileNode(soot.dava.internal.AST.ASTWhileNode) ,public void outArrayRef(soot.jimple.ArrayRef) ,public void outBinopExpr(soot.jimple.BinopExpr) ,public void outCastExpr(soot.jimple.CastExpr) ,public void outDVariableDeclarationStmt(soot.dava.internal.javaRep.DVariableDeclarationStmt) ,public void outDefinitionStmt(soot.jimple.DefinitionStmt) ,public void outExpr(soot.jimple.Expr) ,public void outExprOrRefValueBox(soot.ValueBox) ,public void outInstanceFieldRef(soot.jimple.InstanceFieldRef) ,public void outInstanceInvokeExpr(soot.jimple.InstanceInvokeExpr) ,public void outInstanceOfExpr(soot.jimple.InstanceOfExpr) ,public void outInvokeExpr(soot.jimple.InvokeExpr) ,public void outInvokeStmt(soot.jimple.InvokeStmt) ,public void outNewArrayExpr(soot.jimple.NewArrayExpr) ,public void outNewMultiArrayExpr(soot.jimple.NewMultiArrayExpr) ,public void outRef(soot.jimple.Ref) ,public void outReturnStmt(soot.jimple.ReturnStmt) ,public void outStaticFieldRef(soot.jimple.StaticFieldRef) ,public void outStmt(soot.jimple.Stmt) ,public void outThrowStmt(soot.jimple.ThrowStmt) ,public void outType(soot.Type) ,public void outUnopExpr(soot.jimple.UnopExpr) ,public void outValue(soot.Value) <variables>public boolean DEBUG,boolean verbose