|
Page 2 of 2
Classes and Inheritance
Each of the types used in the queries are actually defined in classes, which contain a constructor, which must hold true for a the type of a value to be that of the class, predicates which evaluate to true or false, and methods which return a typed value. Like object oriented classes, .QL classes support inheritance, and both methods and predicates can be overridden by the subclass.
To see how classes and inheritance work in .QL, we look at the class AnonymousClass in the previous example. It is actually a subclass of NestedClass, which in turn is a subclass of Class and NestedType (.QL allows multiple inheritance).
The classes in this hierarchy is shown below. It uses some prolog-style syntax in some of the method and predicate bodies, but we can ignore those for now. Just note that the special variable result is the return value of a method.
class NestedType extends RefType {
NestedType() { isNestedType(this,T) and @type(T) }
RefType getEnclosingType() { isNestedType(this,result) }
}
class Class extends RefType, @class {
string getIconPath() { result = "icons/class.png" }
predicate isAnonymous() { isAnonymousClass(this) }
predicate isLocal() { isLocalClass(this) }
}
class NestedClass extends NestedType, Class { }
class AnonymousClass extends NestedClass {
AnonymousClass() { this.isAnonymous() }
}
NestedType declares the method getEnclosingType(). The prolog-style predicate
isNestedType(Inner, Outer) is true if the type
Inner is enclosed by the type Outer.
Note that the return value result is used as the Outer parameter and gets the value of the enclosing type.
The method getEnclosing is inherited by NestedClass and subsequently by AnonymousClass.
Class declares the predicate isAnonymous(), which is passed down to AnonymousClass. We see that AnonymousClass uses isAnonymous() in its constructor, which means that a value of type Class is also of type AnonymousClass if and only if isAnonymous() is true for that value.
So instead of writing:
from Class c, Class enclosing
where isNestedType(c, enclosing)
and c.isAnonymous()
and c.getLocation().getNumberOfLines() > 50
select enclosing.getPackage(),
enclosing,
c
we can instead write
from AnonymousClass a
where a.getLocation().getNumberOfLines() > 50
select a.getEnclosingType().getPackage(),
a.getEnclosingType(),
a
and better yet, we can reuse AnonymousClass in other queries!
|