Navigation
Examples First we import the basic data types for representing Java. The model is called M3, and its definition is split acros a generic language independent module called Rascal:analysis/m3/Core and a Java specific part called Rascal:lang/java/m3/Core. Have a look at the documentation of these modules later. For now we will go through using them in a few examples.
rascal>import lang::java::m3::Core;
ok
Then we import the API for extracting an M3 model from an Eclipse project.
rascal>import lang::java::jdt::m3::Core;
ok
Calling the following function generates an enormous value representing everything the Eclipse Java compiler knows about this project:
rascal>myModel = createM3FromEclipseProject(|project://example-project|);
M3: m3(|project://example-project|)[
  @annotations={
    <|java+method:///Apple/edible()|,|java+interface:///java/lang/Override|>,
    <|java+method:///Fruit/edible()|,|java+interface:///java/lang/Override|>
  },
  @typeDependency={
    <|java+method:///HelloWorld/main(java.lang.String%5B%5D)|,|java+class:///java/io/PrintStream|>,
    <|java+method:///Apple/edible()|,|java+primitiveType:///boolean|>,
    <|java+method:///Apple/edible()|,|java+interface:///java/lang/Override|>,
    <|java+method:///Fruit/edible()|,|java+primitiveType:///boolean|>,
    <|java+method:///Fruit/edible()|,|java+interface:///java/lang/Override|>,
    <|java+parameter:///HelloWorld/main(java.lang.String%5B%5D)/args|,|java+array:///java/lang/String%5B%5D|>,
    <|java+method:///HelloWorld/main(java.lang.String%5B%5D)|,|java+class:///java/lang/System|>,
    <|java+class:///Apple|,|java+class:///Fruit|>,
    <|java+method:///IFruit/edible()|,|java+primitiveType:///boolean|>,
    <|java+method:///HelloWorld/main(java.lang.String%5B%5D)|,|java+primitiveType:///void|>,
    <|java+parameter:///HelloWorld/main(java.lang.String%5B%5D)/args|,|java+class:///java/lang/String|>,
    <|java+class:///Fruit|,|java+interface:///IFruit|>
  },
  @methodOverrides={
    <|java+method:///Apple/edible()|,|java+method:///IFruit/edible()|>,
    <|java+method:///Fruit/edible()|,|java+method:///IFruit/edible()|>,
    <|java+method:///Apple/edible()|,|java+method:///Fruit/edible()|>
  },
  @names={
    <"IFruit",|java+interface:///IFruit|>,
    <"HelloWorld",|java+class:///HelloWorld|>,
    <"args",|java+parameter:///HelloWorld/main(java.lang.String%5B%5D)/args|>,
    <"edible",|java+method:///Fruit/edible()|>,
    <"out",|java+field:///java/lang/System/out|>,
    <"edible",|java+method:///Apple/edible()|>,
    <"main",|java+method:///HelloWorld/main(java.lang.String%5B%5D)|>,
    <"String",|java+class:///java/lang/String|>,
    <"edible",|java+method:///IFruit/edible()|>,
    <"println",|java+method:///java/io/PrintStream/println(java.lang.String)|>,
    <"Override",|java+interface:///java/lang/Override|>,
    <"System",|java+class:///java/lang/System|>,
    <"Apple",|java+class:///Apple|>,
    <"Fruit",|java+class:///Fruit|>
  },
  @implements={<|java+class:///Fruit|,|java+interface:///IFruit|>},
  @methodInvocation={<|java+method:///HelloWorld/main(java.lang.String%5B%5D)|,|java+method:///java/io/PrintStream/println(java.lang.String)|>},
  @containment={
    <|java+compilationUnit:///src/IFruit.java|,|java+interface:///IFruit|>,
    <|java+method:///HelloWorld/main(java.lang.String%5B%5D)|,|java+parameter:///HelloWorld/main(java.lang.String%5B%5D)/args|>,
    <|java+class:///Fruit|,|java+method:///Fruit/edible()|>,
    <|java+class:///HelloWorld|,|java+method:///HelloWorld/main(java.lang.String%5B%5D)|>,
    <|java+compilationUnit:///src/Fruit.java|,|java+class:///Fruit|>,
    <|java+compilationUnit:///src/HelloWorld.java|,|java+class:///HelloWorld|>,
    <|java+class:///Apple|,|java+method:///Apple/edible()|>,
    <|java+interface:///IFruit|,|java+method:///IFruit/edible()|>,
    <|java+compilationUnit:///src/Apple.java|,|java+class:///Apple|>
  },
  @documentation={},
  @modifiers={
    <|java+interface:///IFruit|,public()>,
    <|java+method:///HelloWorld/main(java.lang.String%5B%5D)|,static()>,
    <|java+class:///Fruit|,abstract()>,
    <|java+method:///Apple/edible()|,public()>,
    <|java+method:///Fruit/edible()|,public()>,
    <|java+class:///Apple|,public()>,
    <|java+method:///HelloWorld/main(java.lang.String%5B%5D)|,public()>,
    <|java+class:///HelloWorld|,public()>,
    <|java+method:///Fruit/edible()|,abstract()>,
    <|java+class:///Fruit|,public()>
  },
  @fieldAccess={<|java+method:///HelloWorld/main(java.lang.String%5B%5D)|,|java+field:///java/lang/System/out|>},
  @messages=[],
  @uses={
    <|project://example-project/src/Apple.java|(28,5,<2,27>,<2,32>),|java+class:///Fruit|>,
    <|project://example-project/src/HelloWorld.java|(71,6,<4,2>,<4,8>),|java+class:///java/lang/System|>,
    <|project://example-project/src/HelloWorld.java|(82,7,<4,13>,<4,20>),|java+method:///java/io/PrintStream/println(java.lang.String)|>,
    <|project://example-project/src/Fruit.java|(51,8,<3,2>,<3,10>),|java+interface:///java/lang/Override|>,
    <|project://example-project/src/Apple.java|(39,8,<4,2>,<4,10>),|java+interface:///java/lang/Override|>,
    <|project://example-project/src/HelloWorld.java|(78,3,<4,9>,<4,12>),|java+field:///java/lang/System/out|>,
    <|project://example-project/src/Fruit.java|(40,6,<2,39>,<2,45>),|java+interface:///IFruit|>,
    <|project://example-project/src/HelloWorld.java|(52,6,<3,25>,<3,31>),|java+class:///java/lang/String|>
  },
  @types={
    <|java+class:///HelloWorld|,class(
      |java+class:///HelloWorld|,
      [])>,
    <|java+class:///Fruit|,class(
      |java+class:///Fruit|,
      [])>,
    <|java+class:///Apple|,class(
      |java+class:///Apple|,
      [])>,
    <|java+method:///IFruit/edible()|,method(
      |java+method:///IFruit/edible()|,
      [],
      boolean(),
      [])>,
    <|java+parameter:///HelloWorld/main(java.lang.String%5B%5D)/args|,array(
      class(
        |java+class:///java/lang/String|,
        []),
      1)>,
    <|java+method:///HelloWorld/main(java.lang.String%5B%5D)|,method(
      |java+method:///HelloWorld/main(java.lang.String%5B%5D)|,
      [],
      void(),
      [array(
          class(
            |java+class:///java/lang/String|,
            []),
          1)])>,
    <|java+method:///Apple/edible()|,method(
      |java+method:///Apple/edible()|,
      [],
      boolean(),
      [])>,
    <|java+method:///Fruit/edible()|,method(
      |java+method:///Fruit/edible()|,
      [],
      boolean(),
      [])>,
    <|java+interface:///IFruit|,interface(
      |java+interface:///IFruit|,
      [])>
  },
  @extends={<|java+class:///Apple|,|java+class:///Fruit|>},
  @declarations={
    <|java+method:///HelloWorld/main(java.lang.String%5B%5D)|,|project://example-project/src/HelloWorld.java|(28,82,<3,1>,<6,2>)>,
    <|java+compilationUnit:///src/IFruit.java|,|project://example-project/src/IFruit.java|(0,48,<1,0>,<4,2>)>,
    <|java+method:///Apple/edible()|,|project://example-project/src/Apple.java|(38,54,<4,1>,<7,2>)>,
    <|java+parameter:///HelloWorld/main(java.lang.String%5B%5D)/args|,|project://example-project/src/HelloWorld.java|(52,13,<3,25>,<3,38>)>,
    <|java+compilationUnit:///src/HelloWorld.java|,|project://example-project/src/HelloWorld.java|(0,113,<1,0>,<7,2>)>,
    <|java+class:///HelloWorld|,|project://example-project/src/HelloWorld.java|(1,111,<2,0>,<7,1>)>,
    <|java+compilationUnit:///src/Apple.java|,|project://example-project/src/Apple.java|(0,96,<1,0>,<9,2>)>,
    <|java+class:///Apple|,|project://example-project/src/Apple.java|(1,94,<2,0>,<9,1>)>,
    <|java+class:///Fruit|,|project://example-project/src/Fruit.java|(1,95,<2,0>,<5,1>)>,
    <|java+interface:///IFruit|,|project://example-project/src/IFruit.java|(1,46,<2,0>,<4,1>)>,
    <|java+compilationUnit:///src/Fruit.java|,|project://example-project/src/Fruit.java|(0,97,<1,0>,<5,2>)>,
    <|java+method:///IFruit/edible()|,|project://example-project/src/IFruit.java|(28,17,<3,1>,<3,18>)>,
    <|java+method:///Fruit/edible()|,|project://example-project/src/Fruit.java|(50,44,<3,1>,<4,34>)>
  }
]
Next, let's focus on the containment relation. This defines what parts of the source code are parts of which other parts:
rascal>myModel@containment
rel[loc from,loc to]: {
  <|java+compilationUnit:///src/IFruit.java|,|java+interface:///IFruit|>,
  <|java+method:///HelloWorld/main(java.lang.String%5B%5D)|,|java+parameter:///HelloWorld/main(java.lang.String%5B%5D)/args|>,
  <|java+class:///Fruit|,|java+method:///Fruit/edible()|>,
  <|java+class:///HelloWorld|,|java+method:///HelloWorld/main(java.lang.String%5B%5D)|>,
  <|java+compilationUnit:///src/Fruit.java|,|java+class:///Fruit|>,
  <|java+compilationUnit:///src/HelloWorld.java|,|java+class:///HelloWorld|>,
  <|java+class:///Apple|,|java+method:///Apple/edible()|>,
  <|java+interface:///IFruit|,|java+method:///IFruit/edible()|>,
  <|java+compilationUnit:///src/Apple.java|,|java+class:///Apple|>
}
As you can read, classes contain methods, methods contain variables, etc. Classes could also contain other classes (nested classes), and methods can even contain classes (anonymous classes). Let's focus on a specific class, and project what it contains from the relation:
rascal>myModel@containment[|java+class:///HelloWorld|]
set[loc]: {|java+method:///HelloWorld/main(java.lang.String%5B%5D)|}
Let's filter the methods:
rascal>helloWorldMethods = [ e | e <- myModel@containment[|java+class:///HelloWorld|], e.scheme == "java+method"];
list[loc]: [|java+method:///HelloWorld/main(java.lang.String%5B%5D)|]
And we are ready to compute our first metric. How many methods does this class contain?
rascal>import List;
ok
rascal>size(helloWorldMethods)
int: 1
No magic applied! It is just a little query on a model that knows everything about the code. Let's generalize and compute the number of methods for all classes in one big expression. First a function to compute the number for a given class:
rascal>int numberOfMethods(loc cl, M3 model) = size([ m | m <- model@containment[cl], isMethod(m)]);
int (loc, M3): int numberOfMethods(loc, M3);
then we apply this new function to give us a map from classes to integers:
rascal>map[loc class, int methodCount] numberOfMethodsPerClass = (cl:numberOfMethods(cl, myModel) | <cl,_> <- myModel@containment, isClass(cl));
map[loc class, int methodCount]: (
  |java+class:///Fruit|:1,
  |java+class:///Apple|:1,
  |java+class:///HelloWorld|:1
)
how about the number of fields?
rascal>int numberOfFields(loc cl, M3 model) = size([ m | m <- model@containment[cl], isField(m)]);
int (loc, M3): int numberOfFields(loc, M3);
rascal>map[loc class, int fieldCount] numberOfFieldsPerClass = (cl:numberOfFields(cl, myModel) | <cl,_> <- myModel@containment, isClass(cl));
map[loc class, int fieldCount]: (
  |java+class:///Fruit|:0,
  |java+class:///Apple|:0,
  |java+class:///HelloWorld|:0
)
what is the ratio between fields and methods for each class?
rascal>(cl : (numberOfFieldsPerClass[cl] * 1.0) / (numberOfMethodsPerClass[cl] * 1.0) | cl <- classes(myModel))
map[loc, real]: (
  |java+class:///Fruit|:0.,
  |java+class:///Apple|:0.,
  |java+class:///HelloWorld|:0.
)
Is this page unclear, or have you spotted an error? Please add a comment below and help us to improve it. For all other questions and remarks, visit ask.rascal-mpl.org.