1

I have built a program, which takes in a provided ".class" file and parses it using the BCEL, but I'm a bit lost when it comes to using the resulting object to determine the LCOM4 value. I've scoured the whole web, trying to find a proper tutorial about it, but I've been unable so far (I've read the whole javadoc regarding the BCEL as well). So I would like some help with this issue, as in some detailed tutorials or code snippets that would help me understand on how to do it.

PEHLAJ
  • 9,980
  • 9
  • 41
  • 53
  • Is the LCOM4 a function of the method calls? do you absolutely need bcel or could you use, say, ASM? – Maurice Perry May 18 '17 at 07:48
  • Thanks for the reply, and No it is not a function. BCEL is not an absolute necessity, but I would like to try it with using BCEL if possible. – Shehanka Fernando May 18 '17 at 08:26
  • I mean is le LCOM4 calculated based on which methods are called by which method? – Maurice Perry May 18 '17 at 08:43
  • So you already know how to identify dependencies between a method and another method or between a method and a field? – Maurice Perry May 18 '17 at 08:52
  • Oh sorry I misunderstood it. Well LCOM4 is calculated is based upon the grouping. As in all methods within a class must have a connection with each other, either they should be accessing the same variable or calling each other. if every class is connected by any of the before mentioned conditions then it is stated as a single group (Sorry for my bad English) – Shehanka Fernando May 18 '17 at 09:05

1 Answers1

3

OK, let's define a class to represent a group of fields and methods:

public class Group {
    private final Set<String> fields = new HashSet<>();
    private final Set<String> methods = new HashSet<>();

    public Group addFields(String...fields) {
        for (String field: fields) {
            this.fields.add(field);
        }
        return this;
    }

    public Group addMethods(String... methods) {
        for (String method: methods) {
            this.methods.add(method);
        }
        return this;
    }

    public int fields() {
        return fields.size();
    }

    public int methods() {
        return methods.size();
    }

    public boolean intersects(Group other) {
        for (String field: other.fields) {
            if (fields.contains(field)) {
                return true;
            }
        }
        for (String method: other.methods) {
            if (methods.contains(method)) {
                return true;
            }
        }
        return false;
    }

    public void merge(Group other) {
        fields.addAll(other.fields);
        methods.addAll(other.methods);
    }

    @Override
    public String toString() {
        return "Group{" + "fields=" + fields + ", methods=" + methods + '}';
    }
}

We start the process by filling the list of groups with a group for each field defined in the class, then, for each method we build a group with the fields and the methods referenced in the code, then with reduce the list of group by merging and removing each group that intersects the group of the method.

Here's the java code to load the groups of the class. LCOM4 is groups.size():

private List<Group> loadGroups(File file) throws IOException {
    try (InputStream in = new FileInputStream(file)) {
        ClassParser parser = new ClassParser(in, file.getName());
        JavaClass clazz = parser.parse();
        String className = clazz.getClassName();
        ConstantPoolGen cp = new ConstantPoolGen(clazz.getConstantPool());
        List<Group> groups = new ArrayList<Group>();
        for (Field field: clazz.getFields()) {
            groups.add(new Group().addFields(field.getName()));
        }
        for (Method method: clazz.getMethods()) {
            Group group = new Group().addMethods(method.getName());
            Code code = method.getCode();
            InstructionList instrs = new InstructionList(code.getCode());
            for (InstructionHandle ih: instrs) {
                Instruction instr = ih.getInstruction();
                if (instr instanceof FieldInstruction) {
                    FieldInstruction fld = (FieldInstruction)instr;
                    if (fld.getClassName(cp).equals(className)) {
                        group.addFields(fld.getFieldName(cp));
                    }
                } else if (instr instanceof InvokeInstruction) {
                    InvokeInstruction inv = (InvokeInstruction)instr;
                    if (inv.getClassName(cp).equals(className)) {
                        group.addMethods(inv.getMethodName(cp));
                    }
                }
            }
            if (group.fields() > 0 || group.methods() > 1) {
                int i = groups.size();
                while (i > 0) {
                    --i;
                    Group g = groups.get(i);
                    if (g.intersects(group)) {
                        group.merge(g);
                        groups.remove(i);
                    }
                }
                groups.add(group);
            }
        }
        return groups;
    }
}
Maurice Perry
  • 9,261
  • 2
  • 12
  • 24
  • Is this what you were trying to do? – Maurice Perry May 18 '17 at 10:52
  • Thank you very much for the time you took to build this code! It works perfectly and makes a lot of sense with your explanation. Just one more thing if possible. Is it possible to view the method names in each group ? – Shehanka Fernando May 19 '17 at 04:30
  • If you print a group, you will get something like this: `Group{fields=[methods, fields], methods=[addMethods, intersects, methods, merge, addFields, toString, fields, ]}`. You can, of course add getter methods to access the field names and method names. – Maurice Perry May 19 '17 at 05:23
  • Than you! and if its not too much to ask could you have a look at this question too? its basically the same, with a different metric to be calculated. http://stackoverflow.com/questions/44061843/determining-the-efferent-coupling-between-objects-cbo-metric-using-the-parsed – Shehanka Fernando May 19 '17 at 05:33