November 20, 2008, 02:17:44 pm *
News:
 
   Home   Help Search Login Register  
Pages: [1]
  Print  
Author Topic: group by  (Read 1294 times)
Unpublished
Newbie
*
Posts: 6


« on: September 24, 2007, 03:31:06 pm »

Hi,

I want to do some statistics on code metrics. I am interested in "How many methods of my project are in a range less than 5 Lines of code, how many methods are in a range of 5 to 20 lines of code, ... and so on."

I implemented the "range as follows"


Code:
class StatisticMetricCallable extends MetricCallable {
  string lineNumberRange() {
    result = "< 5" and this.getNumberOfLines() < 5 or
    result = "5-20" and (this.getNumberOfLines() > 4 and this.getNumberOfLines() < 21) or
    result = "21-50" and (this.getNumberOfLines() > 20 and this.getNumberOfLines() < 51) or
    result = "51-200" and (this.getNumberOfLines() > 50 and this.getNumberOfLines() < 201) or
    result = "201-500" and (this.getNumberOfLines() > 200 and this.getNumberOfLines() < 501) or
    result = "> 500" and (this.getNumberOfLines() > 500)
}
}

from StatisticMetricCallable c, int n, string range
where c.fromSource()
  and n = c.getNumberOfLines()
  and range = c.lineNumberRange()
  and not(c.hasName("<clinit>"))
  and c instanceof Method
select c.getDeclaringType().getPackage().getName() as Package,
       c.getDeclaringType().getName() as Class,
       c as Method, n as NumberOfLines, range
order by range asc, NumberOfLines desc

I receive a list with all method's, there range and lines of code. But i want to get a table like this

RANGE           COUNT
< 5                  236
5 - 20               98
21 - 50             46
51 - 200           16
201 - 500          1
> 500               0

Is there soemthing like "group by" in .QL - or can I get this result in another way?

Thanks for help and regards
Klaus
« Last Edit: September 26, 2007, 12:47:47 pm by Unpublished » Logged
Mathieu Verbaere
Administrator
Newbie
*****
Posts: 26


« Reply #1 on: September 25, 2007, 11:45:40 am »

Hi Klaus,

you may want to introduce a special class Range as follows:

Code:
predicate interval(int a, int b) {
  a = 0 and b = 4 or
  a = 5 and b = 20 or
  a = 21 and b = 50 or
  a = 51 and b = 200 or
  a = 201 and b = 500 or
  a = 501 and b = 100000000
}

class Range {
  Range() { interval(this,_) }
  int getMin() { result= this }
  int getMax() { interval(this,result) }
  predicate isLowestRange() {
   not exists(Range r | r.getMax() < this.getMin())
  }
  predicate isHighestRange() {
   not exists(Range r | r.getMin() > this.getMax())
  }
  predicate isIntermediateRange() {
   not this.isLowestRange() and not this.isHighestRange()
  }
  string toString() {
    this.isLowestRange() and result = "< " + (this.getMax()+1).toString() or
    this.isHighestRange() and result = "> " + (this.getMin()-1).toString() or
    this.isIntermediateRange() and
      result = this.getMin().toString() + "-" + this.getMax().toString()
  }
}

You can now use Range in any query. Notably:

Code:
class StatisticMetricCallable extends MetricCallable {
  Range lineNumberRange() {
    this.getNumberOfLines() >= result.getMin() and
    this.getNumberOfLines() <= result.getMax()
  }
}

from Range range
select range as RANGE,
   count(StatisticMetricCallable c | range = c.lineNumberRange()
                and c.fromSource()
                and not(c.hasName("<clinit>"))
                and c instanceof Method) as COUNT
                               
For each range, you simply count the number of methods that are in that range.
The results can be viewed in a table or as a chart.
« Last Edit: September 25, 2007, 11:47:34 am by Mathieu Verbaere » Logged
Oege de Moor
Newbie
*
Posts: 30


« Reply #2 on: September 26, 2007, 11:49:08 am »

When exploring metric properties of code, I often find it helpful not to work in terms of ranges. The real question I want to see answered is "what percentage of my code is bad"?

For example, when looking for large methods, it's nice to know the distribution, rather than whether there are a couple of outliers: it's easy to break up a few methods, but if there are large methods throughout, there is a problem throughout the code.

To find such a distribution, I use the query:

from MetricCallable c, int n, float m
where n=c.getNumberOfLines() and
      c.fromSource() and
      m = count(MetricCallable c | c.fromSource() and c.getNumberOfLines()>=n)
          /
          count(MetricCallable c | c.fromSource())
select n,m order by n desc

For each callable length, it tells you the proportion larger than that.

Logged
Unpublished
Newbie
*
Posts: 6


« Reply #3 on: September 26, 2007, 12:47:11 pm »

Hi Oege and Mathieu,

thanks for reply. I like both solutions!

@Oege: I think that your code has to be supplemented by some restrictions in using MetricCallable (only instances of type method and without name <clinit> should be considered. I don't know why but this is the way the "official" examples handle it). The use of your unchanged  code results also in negative LineNumbers .

I changed it like this:

Code:
class MetricMethod extends MetricCallable {
  MetricMethod() {
    this.fromSource() and
    this instanceof Method and
    this.getName() != "<clinit>"
  }
}

from MetricMethod c, int n, float m
where n =  c.getNumberOfLines() and
      m = count(MetricMethod c | c.getNumberOfLines() >= n)
          /
          count(MetricMethod c) * 100
select n as NumberOfLines,
       m as PercentageOfLargerMethods
order by NumberOfLines desc


Regards
Klaus
Logged
Oege de Moor
Newbie
*
Posts: 30


« Reply #4 on: September 27, 2007, 08:16:53 am »

Nice example, thanks!

The issue about negative numbers of lines is of course a bug, and it's been fixed in the release we're preparing. Thanks for bearing with us!

Logged
Pages: [1]
  Print  
 
Jump to: