001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2009 SonarSource SA
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * Sonar is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
019 */
020 package org.sonar.java.bytecode.visitor;
021
022 import java.util.ArrayList;
023 import java.util.HashSet;
024 import java.util.List;
025 import java.util.Set;
026
027 import org.sonar.java.bytecode.asm.AsmClass;
028 import org.sonar.java.bytecode.asm.AsmEdge;
029 import org.sonar.java.bytecode.asm.AsmMethod;
030 import org.sonar.java.bytecode.asm.AsmResource;
031 import org.sonar.squid.api.SourceCodeEdgeUsage;
032 import org.sonar.squid.indexer.SquidIndex;
033 import org.sonar.squid.measures.Metric;
034
035 public class LCOM4Visitor extends BytecodeVisitor {
036
037 private AsmClass asmClass;
038 private List<Set<AsmResource>> unrelatedBlocks = null;
039
040 public LCOM4Visitor(SquidIndex index) {
041 super(index);
042 }
043
044 public void visitClass(AsmClass asmClass) {
045 this.asmClass = asmClass;
046 unrelatedBlocks = new ArrayList<Set<AsmResource>>();
047 }
048
049 public void visitMethod(AsmMethod asmMethod) {
050 if (isMethodElligibleForLCOM4Computation(asmMethod)) {
051 Set<AsmResource> block = getResourceBlockOrCreateIt(asmMethod);
052 for (AsmEdge edge : asmMethod.getOutgoingEdges()) {
053 if (isCallToInternalFieldOrMethod(edge)) {
054 AsmResource toResource = edge.getTo();
055 mergeAsmResourceToBlock(block, toResource);
056 }
057 }
058 }
059 }
060
061 private boolean isMethodElligibleForLCOM4Computation(AsmMethod asmMethod) {
062 return !asmMethod.isAbstract() && !asmMethod.isStatic() && !asmMethod.isConstructor() && !asmMethod.isEmpty()
063 && !asmMethod.isAccessor() && asmMethod.isBodyLoaded();
064 }
065
066 public void leaveClass(AsmClass asmClass) {
067 //filterIsolatedMethods();
068 int lcom4 = unrelatedBlocks.size();
069 if (lcom4 == 0) {
070 lcom4 = 1;
071 }
072
073 getSourceClass(asmClass).add(Metric.LCOM4, lcom4);
074 getSourceClass(asmClass).addData(Metric.LCOM4_BLOCKS, unrelatedBlocks);
075
076 if (isMainPublicClassInFile(asmClass)) {
077 getSourceFile(asmClass).add(Metric.LCOM4, lcom4);
078 getSourceFile(asmClass).addData(Metric.LCOM4_BLOCKS, unrelatedBlocks);
079 }
080 }
081
082 private void mergeAsmResourceToBlock(Set<AsmResource> block, AsmResource toResource) {
083 if (block.contains(toResource)) {
084 return;
085 }
086 Set<AsmResource> otherBlock = getResourceBlock(toResource);
087 if (otherBlock == null) {
088 block.add(toResource);
089
090 } else {
091 block.addAll(otherBlock);
092 unrelatedBlocks.remove(otherBlock);
093 }
094 }
095
096 private boolean isCallToInternalFieldOrMethod(AsmEdge edge) {
097 return edge.getTargetAsmClass() == asmClass
098 && (edge.getUsage() == SourceCodeEdgeUsage.CALLS_FIELD || edge.getUsage() == SourceCodeEdgeUsage.CALLS_METHOD);
099 }
100
101 private Set<AsmResource> getResourceBlockOrCreateIt(AsmResource fromResource) {
102 Set<AsmResource> block = getResourceBlock(fromResource);
103 if (block != null) {
104 return block;
105 }
106 block = new HashSet<AsmResource>();
107 block.add(fromResource);
108 unrelatedBlocks.add(block);
109 return block;
110 }
111
112 private Set<AsmResource> getResourceBlock(AsmResource fromResource) {
113 for (Set<AsmResource> block : unrelatedBlocks) {
114 if (block.contains(fromResource)) {
115 return block;
116 }
117 }
118 return null;
119 }
120 }