Standard Libraries

import Type
import Generics
import Expr

/* ========================================================================= */
/*                      METRICS LIBRARY                                      */
/* ========================================================================= */

/* .QL metrics definitions. References for metrics defined here:

   http://en.wikipedia.org/wiki/Software_package_metrics

   Brian Henderson-Sellers (1996).
   Object-oriented Metrics - Measures of Complexity.
   Prentice Hall. ISBN 0-13-239872-9
   
   Robert Cecil Martin (2002). 
   Agile Software Development: Principles, Patterns and Practices. 
   Pearson Education. ISBN 0-13-597444-5. 

   John Lakos (1996).
   Large-scale C++ Software Design
   Addison-Wesley Professional. ISBN 0-20-163362-0.

   Diomidis Spinellis (2006). 
   Code Quality: The Open Source Perspective. 
   Addison Wesley. ISBN 0-321-16607-8.

   Objecteering:
   http://www.objecteering.com/

   NDepend:
   http://ndepend.com/

   Virtual Machinery JHawk:
   http://www.virtualmachinery.com/jhawkmetrics.htm

   Eclipse metrics plugins:
   http://metrics.sourceforge.net/
   http://eclipse-metrics.sourceforge.net/

   Avoisto Project Analyzer:
   http://www.aivosto.com/project/help/pm-oo-ck.html

   ckjm: Chidamber and Kemerer Java Metrics:
   http://www.spinellis.gr/sw/ckjm/

*/

/** does t depend on dep?
The main challenge with the depends relation is the use of generic types
which may cause reverse dependencies to occur. E.g., a class Foo in
package bar declares a field of type List<Foo>. The paramaterized type
List<Foo> is placed in the same package as List<T>. Therefore we make Foo
not depend on the parameterized type List<Foo> but rather the generic
type List and its argument Foo. Then we can use the definition below
for depends. 

A few subtle points: Parameterized and raw types do not depend on 
any other types in this predicate as we do not want to include them 
in metrics since they introduce false dependencies. The usesType 
predicate is used to decompose a parameterized type into
its generic type and its type arguments.
*/
predicate depends(RefType t, RefType dep) {
   depends(t, dep, _, _)
}

/** does t use dep in any way? */
predicate usesType(Type t, RefType dep) { 
  ((RefType)t).getSourceDeclaration() = dep
  or 
  ((ParameterizedType)t).getATypeArgument() = dep
  or
  (((Array)t).getElementType() = dep) 
}

/** class to explain the reason for a dependency */
class Reason extends Locatable {
  Reason() {
       exists(RefType t | t = this)
    or exists(Field f | f = this)
    or exists(Callable c | c = this) 
    or exists(Call c | c = this)
    or exists(FieldAccess f | f = this)
    or exists(LocalVariableDeclExpr t | t = this) 
    or exists(TypeLiteral t | t = this)
    or exists(Annotation a | a = this)
    or exists(InstanceOfExpr a | a = this)
  }
  string toString() {
       result = ((RefType)this).toString() 
    or result = ((Field)this).toString()
    or result = ((Callable)this).toString()
    or result = ((Call)this).toString()
    or result = ((FieldAccess)this).toString()
    or result = ((LocalVariableDeclExpr)this).toString() 
    or result = ((TypeLiteral)this).toString()
    or result = ((Annotation)this).toString()
    or result = ((InstanceOfExpr)this).toString()
  }
}

/** dependencies with reasons as strings and locatable to click on */
predicate depends(RefType t, RefType dep, string reason, Reason click) {
    not isParameterized(t) and not isRaw(t) and
    (
    (usesType(t.getASupertype(), dep) and reason = "super type" and click = t)
    or
    (usesType(((NestedType)t).getEnclosingType(), dep) and reason = "enclosing type" and 
                click = t)
    or
    (exists(Field f | t.getAField()=f and 
                      usesType(f.getType(), dep) and 
                      reason = "field "+f.getName() and
                      click = f))
    or
    (exists(Method m | m.getDeclaringType() = t and
                        m = m.getSourceDeclaration() and
                        usesType(m.getType(), dep) and 
                        reason = "return of method "+m.getName() and
                        click=m)) 
    or
    (exists(Callable c | c.getDeclaringType() = t and 
             c = c.getSourceDeclaration() and
             usesType(c.getAParamType(), dep) and 
             reason = "parameter of callable "+c.toString() and
             click=c))
    or
    (exists(Exception e, Callable c | c=t.getACallable() and
             c.getAnException()= e and 
             usesType(e.getType(), dep) and 
             reason = c.getName() + " throws "+e.toString() and 
             click = c))
    or
    (exists(Call c, Callable n, Callable m | 
             n = c.getCaller() and
             n.getSourceDeclaration().getDeclaringType()=t and
             m = c.getCallee() and
             usesType(m.getSourceDeclaration().getDeclaringType(), dep) and 
             reason = "call of "+m.getName()+" by "+n.getName() and
             click=c))
    )
    or
    (exists(Field f, FieldRead fr, Callable m | 
             t.getACallable() = m and m = fr.getSite() and
             f = fr.getField() and
             usesType(f.getSourceDeclaration().getDeclaringType(),dep) and
             reason = "read of "+f.getName()+ " by "+ m.getName() and
             click = fr))
    or
    (exists(Field f, FieldWrite fw, Callable m | 
             t.getACallable() = m and m = fw.getSite() and
             f = fw.getField() and
             usesType(f.getSourceDeclaration().getDeclaringType(),dep) and
             reason = "write of "+f.getName()+ " by "+ m.getName() and
             click = fw))
    or
    (exists(LocalVariableDeclExpr ts, Callable c |
            c = ts.getEnclosingCallable() and
            c.getSourceDeclaration().getDeclaringType() = t and
            usesType(ts.getType(),dep) and
            reason = "local variable in "+c.getName() and
            click=ts)) 
    or
    (exists(TypeLiteral l |
            l.getEnclosingCallable().getSourceDeclaration().getDeclaringType() = t and
            usesType(l.getTypeName().getType(),dep) and
            reason = "type literal" and
            click = l))
    or
    (exists(Annotation a | 
            (t = a.getAnnotatedElement() or
             t = ((Member)a.getAnnotatedElement()).getDeclaringType()) and
            (usesType(a.getType(),dep) or
             usesType(a.getAValue().getType(),dep)) and
            reason = "annotation" and
            click = a))
    or
    (exists(InstanceOfExpr ioe |
            t = ioe.getEnclosingCallable().getSourceDeclaration().getDeclaringType() and
            usesType(ioe.getTypeName().getType(),dep) and
            reason = "instanceof" and
            click = ioe))
                                          
}