Standard Libraries

import semmle.code.java.Member
import Commentable

/** A delegate to compute metrics on a callable */
class MetricCallable extends Callable, Commentable {
  
  /** printable representation */
  string toString() { result = Callable.super.toString() }

  /** should the comments for this callable be measured? */
  predicate countDoc(Commentable c) {
     c.fromSource() and
     ((Callable)c).hasModifier("public") and
     c.getNumberOfLinesOfCode() > 25
  }

  /** Dependency of Callables
      One callable "this" depends on another callable "result"
      if "this" makes some call to a method that may end up being "result".
  */
  MetricCallable getADependency() {
     this.polyCalls(result)
  }

  /** Afferent Coupling
      the number of callables that depend on this method.
      This is sometimes called the "fan-in" of a method.
  */
  int getAfferentCoupling() {
     result = count(MetricCallable m | m.getADependency() = this )
  }

  /** Efferent Coupling
      the number of methods that this method depends on
      This is sometimes called the "fan-out" of a method.
  */
  int getEfferentCoupling() {
     result = count(MetricCallable m | this.getADependency() = m)
  }

  /** Number of parameters
      It is generally considered bad practice to have methods with 8
      or more parameters: in those cases, it is often preferable to
      encapsulate the tuple of parameters in a new class.
      To find the problematic methods in your application, run the
      query
           from Method m, int n
           where m.fromSource() and
                 m.getNumberOfParameters() = n and
                 n > 7
           select m.getDeclaringType().getPackage(),
                  m.getDeclaringType(),
                  m,
                  n order by n desc
   */
   int getNumberOfParameters() {
     result = Callable.super.getNumberOfParameters()
   }

   /** Cyclomatic complexity
       the number of branching statements (if, while, do, for, 
       switch, case, catch) plus the number of branching 
       expressions (?, && and ||) plus one.
       Callables with a high cyclomatic complexity (> 10) are
       hard to test and maintain, given their large number of 
       possible execution paths. They should be refactored.
    */
   int getCyclomaticComplexity() {
     result = 
       count(BranchingStmt stmt | stmt.getEnclosingCallable() = this) +
       count(BranchingExpr expr | expr.getEnclosingCallable() = this) +
       1 
   }

   /** the Halstead length of a callable is estimated as the sum of the number of statements
       and expressions within the callable, plus one for the callable itself */
   int getHalsteadLength() { 
      result =   count(Stmt s | s.getEnclosingCallable() = this) 
              + count(Expr e | e.getEnclosingCallable() = this)
              + 1
   }

  /** the Halstead vocabulary of a callable is estimated as the number of unique Halstead IDs
      of all statements and expressions within the callable */
  int getHalsteadVocabulary() { 
    result =   count(string id |    exists(Stmt s | s.getEnclosingCallable() = this and id = s.getHalsteadID())
                                 or exists(Expr e | e.getEnclosingCallable() = this and id = e.getHalsteadID()))
  }
}

/** A branching statement used for the computation of
    cyclomatic complexity */
class BranchingStmt extends Stmt {
  BranchingStmt() {
    this instanceof IfStmt or
    this instanceof WhileStmt or
    this instanceof DoStmt or
    this instanceof ForStmt or
    this instanceof EnhancedForStmt or 
    this instanceof SwitchStmt or
    this instanceof ConstCase or
    this instanceof CatchClause
  }
}

/** A branching expression used for the computation of
    cyclomatic complexity */
class BranchingExpr extends Expr {
  BranchingExpr() {
    this instanceof ConditionalExpr or
    this instanceof AndLogicalExpr or
    this instanceof OrLogicalExpr
  }
}