Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(#215) LCOM4 static fields are now called by FQN #307

Merged
merged 8 commits into from
Jan 25, 2019
14 changes: 12 additions & 2 deletions src/main/java/org/jpeek/skeleton/OpsOf.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
*/
package org.jpeek.skeleton;

import org.cactoos.Text;
import org.cactoos.text.TextOf;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.xembly.Directives;
Expand Down Expand Up @@ -63,16 +65,25 @@ public void visitFieldInsn(final int opcode, final String owner,
final String attr, final String dsc) {
super.visitFieldInsn(opcode, owner, attr, dsc);
this.target.addIf("ops").add("op");
final Text name;
if (opcode == Opcodes.GETFIELD) {
this.target.attr("code", "get");
name = new TextOf(attr);
} else if (opcode == Opcodes.PUTFIELD) {
this.target.attr("code", "put");
name = new TextOf(attr);
} else if (opcode == Opcodes.GETSTATIC) {
this.target.attr("code", "get_static");
name = new QualifiedName(owner, attr);
} else if (opcode == Opcodes.PUTSTATIC) {
this.target.attr("code", "put_static");
name = new QualifiedName(owner, attr);
} else {
name = new TextOf(attr);
}
this.target.set(attr).up().up();
this.target.set(
name
).up().up();
}

@Override
Expand All @@ -86,5 +97,4 @@ public void visitMethodInsn(final int opcode,
.set(owner.replace("/", ".").concat(".").concat(mtd))
.up().up();
}

}
64 changes: 64 additions & 0 deletions src/main/java/org/jpeek/skeleton/QualifiedName.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2017-2019 Yegor Bugayenko
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.jpeek.skeleton;

import org.cactoos.Text;
import org.cactoos.iterable.IterableOf;
import org.cactoos.iterable.Joined;
import org.cactoos.text.JoinedText;
import org.cactoos.text.SplitText;
import org.cactoos.text.TextEnvelope;
import org.cactoos.text.TextOf;
import org.cactoos.text.UncheckedText;

/**
* A fully qualified name of a field, an unambiguous name
* that specifies field without regard
* to the context of the call.
* @author Ilya Kharlamov (ilya.kharlamov@gmail.com)
* @version $Id$
* @since 0.29
*/
public final class QualifiedName extends TextEnvelope {
/**
* Ctor.
* @param owner The class the attribute belongs to
* @param attr The name of the field
*/
public QualifiedName(final String owner, final String attr) {
super(
new UncheckedText(
new JoinedText(
new TextOf("."),
new Joined<Text>(
ilyakharlamov marked this conversation as resolved.
Show resolved Hide resolved
new SplitText(owner, "/"),
new IterableOf<>(
new TextOf(attr)
)
)
)
)
);
}
}
22 changes: 15 additions & 7 deletions src/main/resources/org/jpeek/metrics/LCOM4.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,23 @@ SOFTWARE.
</xsl:template>
<xsl:template match="class">
<xsl:variable name="class_fqn" select="replace(string-join(../@id | @id, '.'), '^\.', '')"/>
<xsl:variable name="A" select="attributes/attribute[@static = 'false']/text()"/>
<xsl:variable name="attrs_fqn">
<xsl:for-each select="attributes/attribute">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:if test="@static='true' and $class_fqn != ''">
<xsl:value-of select="concat($class_fqn, '.')"/>
</xsl:if>
<xsl:value-of select="text()"/>
</xsl:copy>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="A" select="$attrs_fqn/*/text()"/>
<xsl:variable name="a" select="count($A)"/>
<xsl:variable name="M" select="methods/method"/>
<!-- Ctors are not methods -->
<xsl:variable name="M" select="methods/method[@ctor='false']"/>
<xsl:variable name="m" select="count($M)"/>
<!--
@todo #8:30min LCOM4: #156 needs to be fixed in order to avoid confusing access to
fields belonging to other classes as if they actually belong to this class.
Once #156 is fixed, refactor xpaths 'this_attrs' and 'other_attrs' accordingly.
-->
<!--
@todo #216:30min LCOM4: need to finish implementing the rest of the test cases. Only test case
"OneCommonAttribute", "NotCommonAttributes", "NotCommonAttributesWithAllArgsConstructor",
was implemented in this 30min ticket.
Expand Down Expand Up @@ -86,6 +93,7 @@ SOFTWARE.
</xsl:for-each>
</xsl:variable>
<xsl:copy>
<!-- LCOM4 is the number of connected components of G.-->
<xsl:attribute name="value">
<xsl:choose>
<xsl:when test="$m &lt; 2 or $a = 0">
Expand Down
18 changes: 15 additions & 3 deletions src/main/resources/org/jpeek/metrics/LCOM5.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,24 @@ SOFTWARE.
</metric>
</xsl:template>
<xsl:template match="class">
<xsl:variable name="class_fqn" select="replace(string-join(../@id | @id, '.'), '^\.', '')"/>
<xsl:variable name="attrs_fqn">
<xsl:for-each select="attributes/attribute">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:if test="@static='true' and $class_fqn != ''">
<xsl:value-of select="concat($class_fqn, '.')"/>
</xsl:if>
<xsl:value-of select="text()"/>
</xsl:copy>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="methods" select="methods/method"/>
<xsl:variable name="m" select="count($methods)"/>
<xsl:variable name="attributes" select="attributes/attribute/text()"/>
<xsl:variable name="a" select="count($attributes)"/>
<xsl:variable name="attributes" select="$attrs_fqn/*"/>
<xsl:variable name="a" select="count($attributes/text())"/>
<xsl:variable name="attributes_use">
<xsl:for-each select="$attributes">
<xsl:for-each select="$attributes/text()">
<xsl:variable name="attr" select="."/>
<count>
<xsl:value-of select="count($methods[ops/op = $attr])"/>
Expand Down
13 changes: 12 additions & 1 deletion src/main/resources/org/jpeek/metrics/OCC.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,18 @@ SOFTWARE.
</metric>
</xsl:template>
<xsl:template match="class">
<xsl:variable name="A" select="attributes/attribute/text()"/>
<xsl:variable name="class_fqn" select="replace(string-join(../@id | @id, '.'), '^\.', '')"/>
<xsl:variable name="attrs">
<xsl:for-each select="attributes/attribute">
<xsl:copy>
<xsl:if test="@static='true' and $class_fqn != ''">
<xsl:value-of select="concat($class_fqn, '.')"/>
</xsl:if>
<xsl:value-of select="text()"/>
</xsl:copy>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="A" select="$attrs/*/text()"/>
<xsl:variable name="M" select="methods/method"/>
<xsl:variable name="n" select="count($M)"/>
<xsl:variable name="connections">
Expand Down
3 changes: 0 additions & 3 deletions src/test/java/org/jpeek/MetricsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,6 @@ public static Collection<Object[]> targets() {
new Object[] {"OverloadMethods", "TLCOM", 0.0d},
new Object[] {"TwoCommonAttributes", "TLCOM", 4.0d},
new Object[] {"WithoutAttributes", "TLCOM", 1.0d},
new Object[] {"MethodMethodCalls", "LCOM4", 0.6d},
new Object[] {"OneCommonAttribute", "LCOM4", 1d},
new Object[] {"NotCommonAttributes", "LCOM4", 1.5d},
new Object[] {"Bar", "LCC", 0.0d},
new Object[] {"Foo", "LCC", 1.0d},
new Object[] {"MethodMethodCalls", "LCC", 0.1d},
Expand Down
165 changes: 165 additions & 0 deletions src/test/java/org/jpeek/metrics/Lcom4Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2017-2019 Yegor Bugayenko
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package org.jpeek.metrics;

import org.cactoos.list.ListOf;
import org.junit.Ignore;
import org.junit.Test;

/**
* Test case for LCOM4
* LCOM4 = Logical Relatedness of Methods.
* Basically it's a graph disjoint set.
* I.e. it answers the the following question:
* "Into how many independent classes can you split this class?"
* @author Ilya Kharlamov (ilya.kharlamov@gmail.com)
ilyakharlamov marked this conversation as resolved.
Show resolved Hide resolved
* @version $Id$
* @since 0.28
* @checkstyle JavadocMethodCheck (500 lines)
* @checkstyle ClassDataAbstractionCouplingCheck (500 lines)
*/
public final class Lcom4Test {

/**
* Variable from the report.
*/
public static final String PAIRS = "pairs";

/**
* Variable from the report.
*/
public static final String ATTRIBUTES = "attributes";

/**
* Variable from the report.
*/
public static final String METHODS = "methods";

/**
* Path to XSL.
*/
private static final String XSL = "org/jpeek/metrics/LCOM4.xsl";

/**
* Tests the deep dependencies methodFour() -> methodTwo() -> methodOne().
* MethodMethodCalls.java
* - methodOne uses 'num' directly
* - methodTwo uses 'num' indirectly via methodOne
* - methodThree uses 'num' directly
* - methodFour uses 'num' indirectly via methodTwo and methodOne
* - methodFive does not use 'num' (this is an orphan method, ignored)
* Therefore the number of disjoint sets (LCOM4) should be 1
* since all the methods that use num
* @todo #215:30min LCOM4: Graph algorithm to determine disjoint sets.
* Currently we can only identify the dependencies via only one graph hop
* so we can't trace methodFour that uses 'num' indirectly
* via methodTwo and methodOne.
* This one will be tricky to implement,
* Since graph algorithms in XSLT - that will be a challenge.
*/
@Test
@Ignore
ilyakharlamov marked this conversation as resolved.
Show resolved Hide resolved
public void methodMethodCalls() throws Exception {
final MetricBase.Report report = new MetricBase(
Lcom4Test.XSL
).transform(
"MethodMethodCalls"
);
report.assertVariable(
Lcom4Test.METHODS,
new ListOf<>(
"methodOne()",
"methodTwo()",
"methodThree()",
"methodFour()",
"methodFive()"
).size()
);
report.assertVariable(Lcom4Test.ATTRIBUTES, 1);
report.assertValue(1.0F);
ilyakharlamov marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* In OneCommonAttribute.java.
* - methodOne uses variable 'num'
* - methodTwo uses variable 'num'
* So this class only has a single disjoint set AKA (LCOM4)
* This is an ideal LCOM4 value = 1
*/
@Test
public void oneCommonAttribute() throws Exception {
final MetricBase.Report report = new MetricBase(
Lcom4Test.XSL
).transform(
"OneCommonAttribute"
);
report.assertVariable(
Lcom4Test.METHODS,
new ListOf<>(
"methodOne",
"methodTwo"
).size()
);
report.assertVariable(Lcom4Test.ATTRIBUTES, 1);
report.assertVariable(Lcom4Test.PAIRS, 1);
report.assertValue(1.0F);
ilyakharlamov marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* In NotCommonAttributes.java
* - methodOne only uses 'num' variable
* - methodTwo only uses 'anotherNum' variable
* So basically there are two separate disjoint sets,
* or two separate classes under the same umbrella.
* So the value is 2.0
*/
@Test
public void notCommonAttributes() throws Exception {
final MetricBase.Report report = new MetricBase(
Lcom4Test.XSL
).transform(
"NotCommonAttributes"
);
report.assertVariable(Lcom4Test.ATTRIBUTES, 2);
report.assertVariable(Lcom4Test.PAIRS, 0);
report.assertValue(2.0F);
ilyakharlamov marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Should be the same as NotCommonAttributes.
* since constructors are not methods and should be ignored
*/
@Test
public void notCommonAttributesWithAllArgsConstructor() throws Exception {
final MetricBase.Report report = new MetricBase(
Lcom4Test.XSL
).transform(
"NotCommonAttributesWithAllArgsConstructor"
);
report.assertVariable(Lcom4Test.ATTRIBUTES, 2);
report.assertVariable(Lcom4Test.PAIRS, 0);
report.assertValue(2.0F);
ilyakharlamov marked this conversation as resolved.
Show resolved Hide resolved
}
}
Loading