Using JavAssess

This library is composed of five main components (see Implementation). Here we show some examples about how to use them. We start by describing how to use the Introspector, Intercessor and Tester classes, which can be used in an isolated way. Then we explain how an implementation can exploit the whole functionality of the library. This is done by using the Assessor and CodeAssess classes. To do so, we will base our examples on the assessment of a source code that must satisfy the following criteria:

  • Criterion 1: Class Student only has one field and it is of type int: 3 points
  • Criterion 2: Class Student only implements one interface (Person): 3 points
  • Criterion 3: Class PhDStudent extends class Student: 4 points

Introspector

The Introspector class is used to obtain or check syntactical properties of the classes that are loaded using an internal class loader. It is formed by 171 public methods, each of which uses Java Reflexion to access to the information of the classes.

import codeassess.javassessment.Introspector;

public class Example1 {
    public double assess() {
        String[] projectPaths = { "/path/project/" };
        Introspector introspector = new Introspector(projectPaths);
        Class studentClass = introspector.getClass("Student");
        Class phdStudentClass = introspector.getClass("PhDStudent");
        Class personClass = introspector.getClass("Person");

        // Criterion 1
        boolean oneField = introspector.getDeclaredFieldCount(studentClass) == 1;
        Field[] fields = introspector.getDeclaredFields(studentClass);
        boolean isFieldInt = oneField && introspector.checkClass(fields[0], int.class, true);

        // Criterion 2
        boolean oneInterface = introspector.getInterfaceCount(studentClass) == 1;
        boolean implementsPerson = introspector.checkImplements(studentClass, personClass);

        // Criterion 3
        boolean extendsStudent = introspector.checkSuperclass(phdStudentClass, studentClass);

        // Mark
        double mark = 0;
        if (oneField) mark += 1.5;
        if (isFieldInt) mark += 1.5;
        if (oneInterface && implementsPerson) mark += 3;
        if (extendsStudent) mark += 4;
        return mark;
    }
}
                

Intercessor

The Intercessor class is used to modify the source code of the classes that are loaded using an internal class loader. It is formed by 40 public methods, each of which uses the JavaParser library to analyse and modify the source code.

import codeassess.javassessment.Introspector;
import codeassess.javassessment.Intercessor;
import java.lang.reflect.Modifier;

public class Example2 {
    public double assess() {
        String[] projectPaths = { "/path/project/" };
        String[] solutionPaths = { "/path/solution/" };
        Introspector introspector = new Introspector(projectPaths);
        Intercessor intercessor = new Intercessor(projectPaths, solutionPaths);
        Class studentClass = introspector.getClass("Student");
        Class phdStudentClass = introspector.getClass("PhDStudent");
        Class personClass = introspector.getClass("Person");

        // Criterion 1
        intercessor.removeFields(studentClass);
        intercessor.addField(studentClass, Modifier.PROTECTED, int.class, "studCardId");

        // Criterion 2
        intercessor.removeImplements(studentClass);
        intercessor.addImplement(studentClass, personClass);

        // Criterion 3
        intercessor.setSuperclass(phdStudentClass, studentClass);
    }
}
                

Tester

The Tester class is used to execute the test cases that are stored in one Java class. It is formed by 7 public methods that can be used to either execute all the test cases inside the class, or only execute a specific function of the class with specific arguments.

public TestCases {
    public boolean testStudCardId(int studCardId) {
        Student student = new Student(studCardId);
        return student.getStudCardId() == studCardId;
    }
    public boolean testScholarship(int scholarship) {
        Student student = new Student(scholarship);
        return student.getScholarship() == scholarship;
    }
}
                
import codeassess.javassessment.Tester;

public Example3 {
    public boolean test() {
        String[] projectPaths = { "/path/project/" };
        String[] testCasesPaths = { "/path/testCases/", "/path/testCases2/" };
        Tester tester = new Tester(projectPaths, testCasesPaths);

        boolean result1 = tester.executeTestCase("TestCases", "testStudCardId", 1000);
        boolean result2 = tester.executeTestCase("TestCases", "testStudCardId", 1004);
        boolean result3 = tester.executeTestCase("TestCases", "testStudCardId", 1408);

        if (!result1 || !result2 || !result3)
            return false;

        boolean result4 = tester.executeTestCase("TestCases", "testScholarship", 7530);
        boolean result5 = tester.executeTestCase("TestCases", "testScholarship", 2100);
        boolean result6 = tester.executeTestCase("TestCases", "testScholarship", 4000);

        if (!result4 || !result5 || !result6)
            return false;
        return true;
    }
}
                

Exploiting the whole functionality of the library

The JavAssess library is implemented in such a way that the programmer can implement an assessment template (from here on just a Template). In the Template class the programmer specify the criteria that the source code will be assessed with. Finally, the CodeAssess class allows for executing the methods that are implemented in the Template.

Assessor

A Template can be easilly implemented by extending the Assessor class. This class automatically provides the Introspector, Intercessor and Tester classes, so a Template can directly invoke their methods.

import codeassess.javassessment.Assessor;

public Template extends Assessor {
    public List getCriteria() {
        List criteria = new ArrayList();
        criteria.add(new Object[]{ "criterion1", 3.0 });
        criteria.add(new Object[]{ "criterion2", 3.0 });
        criteria.add(new Object[]{ "criterion3", 4.0 });
        return criteria;
    }

    public double criterion1() {
        Class studentClass = introspector.getClass("Student");
        boolean oneField = introspector.getDeclaredFieldCount(studentClass) == 1;
        Field[] fields = introspector.getDeclaredFields(studentClass);
        boolean isFieldInt = oneField && introspector.checkClass(fields[0], int.class, true);

        if (oneField && isFieldInt) return 3.0;

        intercessor.removeFields(studentClass);
        intercessor.addField(studentClass, Modifier.PROTECTED, int.class, "studCardId");
        super.refresh();

        if (oneField || isFieldInt) return 1.5;
        
        return 0.0;
    }
    public double criterion2() {
        Class studentClass = introspector.getClass("Student");
        Class personClass = introspector.getClass("Person");
        boolean oneInterface = introspector.getInterfaceCount(studentClass) == 1;
        boolean implementsPerson = introspector.checkImplements(studentClass, personClass);

        if (oneInterface && implementsPerson) return 3.0;

        intercessor.removeImplements(studentClass);
        intercessor.addImplement(studentClass, personClass);
        super.refresh();

        return 0.0;
    }
    public double criterion3() {
        Class studentClass = introspector.getClass("Student");
        Class phdStudentClass = introspector.getClass("PhDStudent");
        boolean extendsStudent = introspector.checkSuperclass(phdStudentClass, studentClass);

        if (extendsStudent) return 4.0;

        intercessor.setSuperclass(phdStudentClass, studentClass);
        super.refresh();

        return 0.0;
    }
}
                

CodeAssess

The CodeAssess class is used to assess a given program by using an Assessor object. CodeAssess just automates the assessment by executing the appropriate methods of the assessor. CodeAssess internally uses a class loader to load the classes of the project. When these classes change, the refresh method can be used to update the current classes to reference the actual ones.

import codeassess.CodeAssess;

public class Exemple4 {
    public double assess() {
        String templatePath = "/path/";
        String templateName = "InehitanceExercise.java"
        String[] libraries = { "/path/library1.jar", "/path/library2.jar" };
        String[] projectPaths = { "/path/project/" };
        String[] testCasesPaths = { "/path/testCases/", "/path/testCases2/" };
        String[] solutionPaths = { "/path/solution/" };
        CodeAssess codeAssess = new CodeAssess(templatePath, templateName,
                libraries, projectPaths, testCasesPaths, solutionPaths);

        List criteria = (List) codeAssess.execute("getCriteria");
        double finalMark = 0.0;

        for (Object[] criterion : criteria) {
            String criterionName = (String) criterion[0];
            Object[] arguments = { };
            finalMark += (Double) codeAssess.execute(criterionName, arguments);
        }

        return finalMark;
    }
}