-
Notifications
You must be signed in to change notification settings - Fork 81
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
added ccmCalculus especially to find ncc value for ccm metric. The lo… #548
base: master
Are you sure you want to change the base?
Changes from 50 commits
aebacaa
c9293ab
fd730b6
4443ead
e0f8a2f
0ce54fd
aaa584a
9557ee8
3b47211
144e4e1
1f7f100
dd92650
61dbe52
cdc7ae4
5017e4a
1071ee1
5f37cbb
5258ae7
346d3d2
5a5a305
83a8137
d051565
d62314d
0874e82
d06c483
d4ffcde
0a64225
4dc4951
fe2b446
969c63e
f8e30c1
0bfe7a7
cb0d747
e0f6596
71dedfe
db39bd7
6bbc1ee
8084432
d26f4e7
47254cc
a8684bd
f6489cb
93668c3
4f56dfb
762b62f
ddce255
b4cf48b
debef9d
39b8f2f
da5370e
69c8980
202e99e
bb8e09f
ec8dd38
b6404bc
4b0e358
52c404c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,21 +24,33 @@ | |
package org.jpeek.calculus.java; | ||
|
||
import com.jcabi.xml.XML; | ||
import com.jcabi.xml.XSL; | ||
import com.jcabi.xml.XSLDocument; | ||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import javax.xml.parsers.DocumentBuilderFactory; | ||
import javax.xml.parsers.ParserConfigurationException; | ||
import org.cactoos.io.ResourceOf; | ||
import org.cactoos.io.UncheckedInput; | ||
import org.cactoos.text.FormattedText; | ||
import org.cactoos.text.Joined; | ||
import org.jpeek.XslReport; | ||
import org.jpeek.calculus.Calculus; | ||
import org.w3c.dom.Document; | ||
import org.w3c.dom.Element; | ||
import org.w3c.dom.Node; | ||
|
||
/** | ||
* CCM metric Java calculus. | ||
* This class implements the Calculus interface to provide functionality | ||
* for computing the CCM metric for Java code. | ||
* @since 0.30.25 | ||
*/ | ||
public final class Ccm implements Calculus { | ||
|
||
@Override | ||
public XML node( | ||
final String metric, | ||
|
@@ -52,57 +64,208 @@ public XML node( | |
).toString() | ||
); | ||
} | ||
return Ccm.withFixedNcc( | ||
new XSLDocument( | ||
new UncheckedInput( | ||
new ResourceOf("org/jpeek/metrics/CCM.xsl") | ||
).stream() | ||
).transform(skeleton), | ||
skeleton | ||
final XSLDocument doc = new XSLDocument( | ||
new UncheckedInput( | ||
new ResourceOf("org/jpeek/metrics/CCM.xsl") | ||
).stream() | ||
); | ||
final XML meta = addMetaInformation(skeleton, params); | ||
return doc.transform(meta); | ||
} | ||
|
||
/** | ||
* Updates the transformed xml with proper NCC value. | ||
* @param transformed The transformed XML skeleton. | ||
* @param skeleton XML Skeleton | ||
* @return XML with fixed NCC. | ||
* Adds meta information to the skeleton XML document. | ||
* This method modifies the skeleton XML document by adding meta information | ||
* about the computed CCM metric. | ||
* @param skeleton The skeleton XML document representing the code structure. | ||
* @param params Parameters for the computation. | ||
* @return The modified XML document containing meta information. | ||
*/ | ||
private static XML withFixedNcc(final XML transformed, final XML skeleton) { | ||
final List<XML> packages = transformed.nodes("//package"); | ||
for (final XML elt : packages) { | ||
final String pack = elt.xpath("/@id").get(0); | ||
final List<XML> classes = elt.nodes("//class"); | ||
for (final XML clazz : classes) { | ||
Ccm.updateNcc(skeleton, pack, clazz); | ||
private static XML addMetaInformation(final XML skeleton, final Map<String, Object> params) { | ||
try { | ||
final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder() | ||
.newDocument(); | ||
final Element meta = doc.createElement("meta"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it is not required to change XML document and add some data, it is easier to call XSL with parameter, so you need to read ncc value in XSL and use it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You do not understand, we can run XSL for class skeleton with parameter cmm, so we do not need to create package and all others inside, as all this arledy implemented and can be changed in case if it is needed in xsl using common way. it is absolutely not readable in code, xsl is much clear |
||
final List<XML> packages = skeleton.nodes("//package"); | ||
for (final XML pack : packages) { | ||
final Element tag = doc.createElement("package"); | ||
tag.setAttribute( | ||
"id", pack.node().getAttributes().getNamedItem("id").getNodeValue() | ||
); | ||
final List<XML> classes = pack.nodes("class"); | ||
for (final XML clazz: classes) { | ||
final Element sub = doc.createElement("class"); | ||
sub.appendChild(addNccTag(doc, clazz, params)); | ||
sub.setAttribute( | ||
"id", | ||
clazz.node().getAttributes().getNamedItem("id").getNodeValue() | ||
); | ||
tag.appendChild(sub); | ||
} | ||
meta.appendChild(tag); | ||
} | ||
final XSL modifier = new XSLDocument( | ||
XslReport.class.getResourceAsStream( | ||
"xsl/meta-info.xsl" | ||
) | ||
).with("meta", meta); | ||
return modifier.transform(skeleton); | ||
} catch (final ParserConfigurationException ex) { | ||
throw new IllegalStateException(ex); | ||
} | ||
return transformed; | ||
} | ||
|
||
/** | ||
* Updates the xml node of the class with proper NCC value. | ||
* @param skeleton XML Skeleton | ||
* @param pack Package name | ||
* @param clazz Class node in the resulting xml | ||
* @todo #449:30min Implement NCC calculation with `XmlGraph` and use this | ||
* class to fix CCM metric (see issue #449). To do this, this class, once | ||
* it works correctly, should be integrated with XSL based calculuses in | ||
* `XslReport` (see `todo #449` in Calculus). Write a test to make sure | ||
* the metric is calculated correctly. Also, decide whether the | ||
* whole CCM metric should be implemented in Java, or only the NCC part. | ||
* Update this `todo` accordingly. | ||
* Adds NCC (Number of Component Connections) tag to the XML document. | ||
* This method calculates the NCC for a given class and adds it as a tag to the XML document. | ||
* @param doc The XML document to which the NCC tag will be added. | ||
* @param clazz The XML representation of the class. | ||
* @param params Parameters for the computation (unused). | ||
* @return The NCC node. | ||
*/ | ||
private static void updateNcc( | ||
final XML skeleton, final String pack, final XML clazz | ||
private static Node addNccTag(final Document doc, final XML clazz, | ||
final Map<String, Object> params | ||
) { | ||
throw new UnsupportedOperationException( | ||
new Joined( | ||
"", | ||
skeleton.toString(), | ||
pack, | ||
clazz.toString() | ||
).toString() | ||
); | ||
final Element ncc = doc.createElement("ncc"); | ||
ncc.appendChild(doc.createTextNode(calculateComponents(clazz, params).toString())); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now incapsulation is better, but according to OOP we should have |
||
return ncc; | ||
} | ||
|
||
/** | ||
* Calculates the number of components for a given class. | ||
* This method calculates the number of components for a class using the Union-Find algorithm. | ||
* @param clazz The XML representation of the class. | ||
* @param params Parameters for the computation. | ||
* @return The number of components. | ||
*/ | ||
private static Integer calculateComponents(final XML clazz, final Map<String, Object> params) { | ||
starkda marked this conversation as resolved.
Show resolved
Hide resolved
|
||
final Map<String, List<String>> connections = new HashMap<>(); | ||
final Map<String, String> parents = new HashMap<>(); | ||
final List<XML> allowed = new ArrayList<>(0); | ||
for (final XML method : clazz.nodes("methods/method")) { | ||
final String name = method.xpath("@name").get(0); | ||
if (isConstructorExcluded(params, method) || isStaticMethodExcluded(params, method)) { | ||
continue; | ||
} | ||
allowed.add(method); | ||
parents.put(name, name); | ||
} | ||
for (final XML method : allowed) { | ||
final String name = method.xpath("@name").get(0); | ||
final List<XML> ops = method.nodes("ops/op"); | ||
for (final XML operation : ops) { | ||
final String code = operation.xpath("@code").get(0); | ||
if (code.equals("call")) { | ||
final String classpath = operation.nodes("name").get(0).node().getTextContent(); | ||
final List<String> splits = Arrays.asList(classpath.split("\\.")); | ||
if (parents.keySet().contains(splits.get(splits.size() - 1))) { | ||
UnionFind.unite(name, splits.get(splits.size() - 1), parents); | ||
} | ||
} else { | ||
final String var = operation.node().getTextContent(); | ||
if (connections.containsKey(var)) { | ||
connections.get(var).add(name); | ||
} else { | ||
final List<String> init = new ArrayList<>(0); | ||
init.add(name); | ||
connections.put(var, init); | ||
} | ||
} | ||
} | ||
} | ||
return UnionFind.runUnionFind(parents, connections); | ||
} | ||
|
||
/** | ||
* Checks if a static method should be excluded based on parameters. | ||
* @param params Parameters for filtering. | ||
* @param method The method XML node. | ||
* @return True if the method should be excluded, false otherwise. | ||
*/ | ||
private static boolean isStaticMethodExcluded(final Map<String, Object> params, | ||
final XML method) { | ||
return !params.containsKey("include-static-methods") && method.xpath("@static").get(0) | ||
.equals("true"); | ||
} | ||
|
||
/** | ||
* Checks if a constructor should be excluded based on parameters. | ||
* @param params Parameters for filtering. | ||
* @param method The method XML node. | ||
* @return True if the constructor should be excluded, false otherwise. | ||
*/ | ||
private static boolean isConstructorExcluded(final Map<String, Object> params, | ||
final XML method) { | ||
return !params.containsKey("include-ctors") && method.xpath("@ctor").get(0).equals("true"); | ||
} | ||
|
||
/** | ||
* Utility class implementing the Union-Find algorithm. | ||
* The UnionFind class provides methods to perform the Union-Find algorithm, | ||
* which is used to calculate the number of components in a given structure. | ||
* @since 0.30.25 | ||
*/ | ||
private static class UnionFind { | ||
starkda marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/** | ||
* Performs the Union-Find algorithm to calculate the number of components. | ||
* This method implements the Union-Find algorithm to calculate the number of components. | ||
* @param parents The map representing the parent relationship. | ||
* @param connections The map representing the connections between variables and methods. | ||
* @return The number of components. | ||
*/ | ||
private static Integer runUnionFind(final Map<String, String> parents, | ||
final Map<String, List<String>> connections | ||
) { | ||
final Set<String> unique = new HashSet<>(parents.values()); | ||
int answer = unique.size(); | ||
for (final List<String> conns : connections.values()) { | ||
final String initial = conns.get(0); | ||
for (final String connectable : conns) { | ||
if (!parents.get(initial).equals(parents.get(connectable))) { | ||
answer -= 1; | ||
} | ||
unite(initial, connectable, parents); | ||
} | ||
} | ||
return answer; | ||
} | ||
|
||
/** | ||
* Gets the parent of a node using the Union-Find algorithm. | ||
* This method retrieves the parent of a node using the Union-Find algorithm. | ||
* @param node The node whose parent is to be found. | ||
* @param parents The map representing the parent relationship. | ||
* @return The parent of the node. | ||
*/ | ||
private static String getParent(final String node, final Map<String, String> parents) { | ||
String ancestor = node; | ||
while (!parents.get(ancestor).equals(ancestor)) { | ||
ancestor = parents.get(ancestor); | ||
} | ||
String current = node; | ||
while (!parents.get(current).equals(current)) { | ||
final String temp = parents.get(current); | ||
parents.put(current, ancestor); | ||
current = temp; | ||
} | ||
return ancestor; | ||
} | ||
|
||
/** | ||
* Unites two nodes using the Union-Find algorithm. | ||
* This method unites two nodes using the Union-Find algorithm. | ||
* @param node The first node. | ||
* @param son The second node. | ||
* @param parents The map representing the parent relationship. | ||
*/ | ||
private static void unite(final String node, final String son, | ||
final Map<String, String> parents | ||
) { | ||
final String root = getParent(node, parents); | ||
final String attachable = getParent(son, parents); | ||
if (!root.equals(attachable)) { | ||
parents.put(attachable, root); | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
SOFTWARE. | ||
--> | ||
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"> | ||
<xsl:template match="skeleton/meta"/> | ||
<xsl:template match="skeleton"> | ||
<metric> | ||
<xsl:apply-templates select="@*"/> | ||
|
@@ -40,7 +41,11 @@ SOFTWARE. | |
<xsl:apply-templates select="node()"/> | ||
</metric> | ||
</xsl:template> | ||
<xsl:variable name="met" select="/skeleton/meta"/> | ||
<xsl:template match="class"> | ||
<xsl:variable name="currentClassId" select="@id"/> | ||
<xsl:variable name="currentPackageId" select="../@id"/> | ||
<xsl:variable name="currentClassNcc" select="$met/package[@id = $currentPackageId]/class[@id = $currentClassId]/ncc"/> | ||
<xsl:variable name="methods" select="methods/method[@ctor='false']"/> | ||
<xsl:variable name="edges"> | ||
<xsl:for-each select="$methods"> | ||
|
@@ -56,13 +61,21 @@ SOFTWARE. | |
<xsl:value-of select="$other/@name"/> | ||
</method> | ||
</edge> | ||
<edge> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should be reverted as I understand |
||
<method> | ||
<xsl:value-of select="$other/@name"/> | ||
</method> | ||
<method> | ||
<xsl:value-of select="$method/@name"/> | ||
</method> | ||
</edge> | ||
</xsl:if> | ||
</xsl:for-each> | ||
</xsl:for-each> | ||
</xsl:variable> | ||
<xsl:copy> | ||
<xsl:variable name="nc" select="count($edges/edge)"/> | ||
<xsl:variable name="ncc" select="count(distinct-values($edges/edge/method/text()))"/> | ||
<xsl:variable name="nc" select="count($edges/edge) div 2"/> | ||
<xsl:variable name="ncc" select="$currentClassNcc"/> | ||
<xsl:variable name="nmp" select="(count($methods) * (count($methods) - 1)) div 2"/> | ||
<xsl:attribute name="value"> | ||
<xsl:choose> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not understand why you ignore the comment below. This part of the code is very difficult to support. You can add child element to the class without coping all this structure.