Skip to content

Java examples

These examples call the public methods of the CLDK Python SDK. Each snippet runs against a real codebase, the recurring sample Apache Commons CLI (project_path="commons-cli"). Each snippet shows one feature and the output that the SDK returns. To adapt an example, use your own project path.

Before you query a codebase, build a CLDK analysis object for your Java project. The analysis_level parameter controls how deeply the backend parses the code. symbol_table, the default, finds classes, methods, and fields. call_graph also builds caller and callee edges.

from cldk import CLDK
from cldk.analysis import AnalysisLevel
# Symbol table only: classes, methods, fields
analysis = CLDK.java(
project_path="commons-cli",
analysis_level=AnalysisLevel.symbol_table,
)
# Call graph included: all of the above plus caller/callee edges
analysis_cg = CLDK.java(
project_path="commons-cli",
analysis_level=AnalysisLevel.call_graph,
)

Symbol table: classes, methods, and fields

Section titled “Symbol table: classes, methods, and fields”

After you build the analysis object, query the symbol table to list classes, methods, and fields by name.

List every class in the symbol table of the project.

classes = analysis.get_classes()
print(len(classes))
# 145
# All classes keyed by qualified name
for name, jtype in classes.items():
print(name, "->", jtype)
# org.apache.commons.cli.Option -> <JType ...>
# org.apache.commons.cli.Options -> <JType ...>
# ...

Get the metadata for one class by its fully qualified name.

jtype = analysis.get_class("org.apache.commons.cli.Options")
print(jtype.qualified_name, "is_interface:", jtype.is_interface, "is_abstract:", "abstract" in jtype.modifiers)
# org.apache.commons.cli.Options is_interface: False is_abstract: False

List every method declared on a class.

methods = analysis.get_methods_in_class("org.apache.commons.cli.Options")
print(len(methods))
# 29
for signature, method in methods.items():
print(method.declaration)
# public Options addOption(Option opt)
# public Options addOption(String opt, String longOpt, String description)
# public Option getOption(String opt)
# ...

Get the full declaration and source body of a method by class name and method signature.

method = analysis.get_method(
"org.apache.commons.cli.Options",
"addOption(org.apache.commons.cli.Option)"
)
print(method.declaration)
print(method.code)
# public Options addOption(Option opt)
#
# {
# String key = opt.getKey();
# if ((key == null) || (key.trim().length() == 0)) {
# throw new IllegalArgumentException("The key for option cannot be null or empty");
# }
# this.shortOpts.put(key, opt);
# this.longOpts.put(opt.getLongOpt(), opt);
# this.optionGroups.put(opt, opt);
# return this;
# }

Get the list of parameters for a method, with their types and their location in the source. New in 2.0 In CLDK 1.5.0, get_method_parameters returns parameter names only, as strings.

params = analysis.get_method_parameters(
"org.apache.commons.cli.Options",
"addOption(org.apache.commons.cli.Option)"
)
print(len(params))
# 1
for param in params:
print(param.name, param.type)
# opt Option

Get every field declaration on a class.

fields = analysis.get_fields("org.apache.commons.cli.Option")
print(len(fields))
# 8
for field in fields:
print(field.name, "type:", field.type)
# longOpt type: String
# opt type: String
# description type: String
# ...

Call graph: caller and callee relationships

Section titled “Call graph: caller and callee relationships”

To query caller and callee relationships, build the analysis at call_graph level. This level builds the networkx.DiGraph that the following sections use.

Get the full call graph as a networkx.DiGraph. Edges point from caller to callee. Weights and metadata live in the edge attributes.

from cldk.analysis import AnalysisLevel
analysis = CLDK.java(
project_path="commons-cli",
analysis_level=AnalysisLevel.call_graph,
)
cg = analysis.get_call_graph()
print(cg.number_of_nodes(), "methods,", cg.number_of_edges(), "edges")
# 421 methods, 638 edges
# Edge data includes call type and calling line numbers
for source, target, data in cg.edges(data=True):
print(source, "->", target, "type:", data["type"], "lines:", data["calling_lines"])
# org.apache.commons.cli.Options.addOption -> org.apache.commons.cli.Option.getKey CALL_DEP lines: [68]
# ...

Find every method that calls a target method. This query supports impact analysis: it shows which callers a change to the target affects.

callers = analysis.get_callers(
"org.apache.commons.cli.Options",
"addOption(org.apache.commons.cli.Option)"
)
print(len(callers["caller_details"]))
# 18
for detail in callers["caller_details"]:
print(detail["caller_method"].klass, detail["caller_method"].method.signature)
# org.apache.commons.cli.Options org.apache.commons.cli.Options.addOption(String, boolean, String)
# org.apache.commons.cli.DefaultParser org.apache.commons.cli.DefaultParser.handleOption(...)
# ...

Find every method that a target method calls. This query supports dependency analysis: it shows the methods that a given method depends on.

callees = analysis.get_callees(
"org.apache.commons.cli.Options",
"addOption(org.apache.commons.cli.Option)"
)
print(len(callees["callee_details"]))
# 2
for detail in callees["callee_details"]:
print(detail["callee_method"].klass, detail["callee_method"].method.signature)
# org.apache.commons.cli.Option org.apache.commons.cli.Option.getKey()
# java.util.HashMap java.util.HashMap.put(Object, Object)
# ...

Class relationships: inheritance and interfaces

Section titled “Class relationships: inheritance and interfaces”

Query the inheritance hierarchy to understand class structure and polymorphism.

Get every class that directly extends or implements a target type. The result does not include transitive subclasses.

subclasses = analysis.get_sub_classes("java.io.Serializable")
print(len(subclasses))
# 15
for name, jtype in subclasses.items():
print(name)
# org.apache.commons.cli.Option
# org.apache.commons.cli.Options
# ...

Get the direct superclass of a target class. If the class extends only java.lang.Object, the list is empty.

extended = analysis.get_extended_classes(
"org.apache.commons.cli.AlreadySelectedException"
)
print(len(extended))
# 1
print(extended[0])
# org.apache.commons.cli.ParseException

Get every interface that a class declares it implements.

interfaces = analysis.get_implemented_interfaces(
"org.apache.commons.cli.DefaultParser"
)
print(len(interfaces))
# 1
print(interfaces[0])
# org.apache.commons.cli.CommandLineParser

Extract every comment (line, block, and Javadoc) from the codebase to find the documentation and the intent.

Get every comment in the project, organized by file.

comments = analysis.get_all_comments()
print(len(comments), "files with comments")
# 145 files with comments
for file_path, comment_list in comments.items():
print(f"{file_path}: {len(comment_list)} comments")
for comment in comment_list[:2]:
print(" ", comment.content[:60])
# src/main/java/org/apache/commons/cli/Option.java: 12 comments
# The long representation of the option.
# Specified in OptionBuilder.

Get the Javadoc documentation on classes and methods.

docstrings = analysis.get_all_docstrings()
print(len(docstrings), "files with docstrings")
# 78 files with docstrings
for file_path, doc_list in docstrings.items():
print(f"{file_path}:")
for doc in doc_list[:1]:
print(doc.content)
# src/main/java/org/apache/commons/cli/Option.java:
# /**
# * Option class to store and manipulate a command line option.
# */

Use the call graph with networkx to answer reachability questions, such as whether execution can flow from one method to another.

import networkx as nx
from cldk.analysis import AnalysisLevel
analysis = CLDK.java(
project_path="commons-cli",
analysis_level=AnalysisLevel.call_graph,
)
cg = analysis.get_call_graph()
# Is the target method reachable from the source method?
source = "org.apache.commons.cli.Options.addOption(org.apache.commons.cli.Option)"
target = "org.apache.commons.cli.Option.getKey()"
is_reachable = nx.has_path(cg, source, target)
print(is_reachable)
# True
# All nodes reachable from a source
source = "org.apache.commons.cli.DefaultParser.parse(org.apache.commons.cli.Options, java.lang.String[])"
reachable_nodes = nx.descendants(cg, source)
print(len(reachable_nodes), "methods reachable from parse()")
# 67 methods reachable from parse()
# Find all strongly connected components (cycles in call graph)
cycles = list(nx.strongly_connected_components(cg))
print(len(cycles), "strongly connected components")
for cycle in cycles[:1]:
print(cycle)
# frozenset(['org.apache.commons.cli.DefaultParser.handleOption(...)',
# 'org.apache.commons.cli.Options.getOption(String)'])

Find the public methods that are entrypoints into the application, for example servlet handlers, REST endpoints, and main methods.

entry_methods = analysis.get_entry_point_methods()
print(len(entry_methods), "classes with entry points")
# 64 classes with entry points
for class_name, methods in entry_methods.items():
for sig, method in methods.items():
print(class_name, sig)
# org.apache.commons.cli.DefaultParser parse(String[])
# org.apache.commons.cli.HelpFormatter printHelp(String)
# ...