Extrayez les informations de classe et de méthode à partir du code d'octet Java et enregistrez-le dans Sqlite. Décrivez le diagramme de classe et le graphe d'appel en utilisant les informations enregistrées ci-dessus.
Si vous pouvez utiliser doxygen + graphviz, vous devez l'utiliser.
・ Windows10 ・ Java8 ・ Bcel-6.3.1. ・ Sqlite-jdbc-3.27.2.1 ・ Plantuml.jar
bcel L'API BCEL (Byte Code Engineering Library) est une boîte à outils pour l'analyse statique et la création dynamique de fichiers de classe Java. FindBug utilise BCEL pour effectuer une analyse statique à partir de fichiers de classe.
https://commons.apache.org/proper/commons-bcel/manual/introduction.html
JavaSE BCEL https://hondou.homedns.org/pukiwiki/index.php?JavaSE%20BCEL
En outre, le codeToString suivant sera utile pour savoir comment interpréter le binaire de code d'octet. https://github.com/llmhyy/commons-bcel/blob/master/src/main/java/org/apache/bcel/classfile/Utility.java
Sqlite-JDBC Vous pouvez le télécharger à partir de la page suivante avec JDBC qui exploite SQLite.
Télécharger https://bitbucket.org/xerial/sqlite-jdbc/downloads/
package sqlitesample;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.sqlite.Function;
public class SqliteSample {
public static void main(String[] args) throws ClassNotFoundException {
System.out.println("start-----");
// load the sqlite-JDBC driver using the current class loader
Class.forName("org.sqlite.JDBC");
Connection connection = null;
Statement statement = null;
ResultSet rs = null;
PreparedStatement pstmt = null;
try
{
// create a database connection
// Connection connection = DriverManager.getConnection("jdbc:sqlite:C:/work/mydatabase.db");
// Connection connection = DriverManager.getConnection("jdbc:sqlite::memory:");
connection = DriverManager.getConnection("jdbc:sqlite:sample.db");
connection.setAutoCommit(true);
statement = connection.createStatement();
statement.setQueryTimeout(30); // set timeout to 30 sec.
System.out.println("create table -----");
statement.executeUpdate("drop table if exists person");
statement.executeUpdate("create table person (id integer, name string)");
statement.executeUpdate("insert into person values(1, 'leo')");
statement.executeUpdate("insert into person values(2, 'yui')");
rs = statement.executeQuery("select * from person");
while(rs.next())
{
// read the result set
System.out.println("name = " + rs.getString("name"));
System.out.println("id = " + rs.getInt("id"));
}
rs.close();
rs = null;
System.out.println("update -----");
pstmt = connection.prepareStatement("update person set name = ? where id = ?");
pstmt.setString(1, "ole");
pstmt.setInt(2, 1);
pstmt.executeUpdate();
rs = statement.executeQuery("select * from person");
while(rs.next())
{
// read the result set
System.out.println("name = " + rs.getString("name"));
System.out.println("id = " + rs.getInt("id"));
}
rs.close();
rs = null;
System.out.println("Transactions -----");
connection.setAutoCommit(false);
statement.executeUpdate("insert into person values(3, 'zoo')");
rs = statement.executeQuery("select * from person");
while(rs.next())
{
// read the result set
System.out.println("name = " + rs.getString("name"));
System.out.println("id = " + rs.getInt("id"));
}
rs.close();
rs = null;
System.out.println("rollback -----");
connection.rollback();
rs = statement.executeQuery("select * from person");
while(rs.next())
{
// read the result set
System.out.println("name = " + rs.getString("name"));
System.out.println("id = " + rs.getInt("id"));
}
rs.close();
rs = null;
System.out.println("function -----");
Function.create(connection, "total", new Function() {
@Override
protected void xFunc() throws SQLException
{
int sum = 0;
for (int i = 0; i < args(); i++)
sum += value_int(i);
result(sum);
}
});
rs = statement.executeQuery("select total(1, 2, 3, 4, 5)");
while(rs.next())
{
// read the result set
System.out.println("total(1,2,3,4,5) = " + rs.getInt(1));
}
rs.close();
rs = null;
}
catch(SQLException e)
{
// if the error message is "out of memory",
// it probably means no database file is found
System.err.println(e.getMessage());
}
finally
{
try
{
if(rs != null)
{
rs.close();
}
if(pstmt != null)
{
pstmt.close();
}
if(statement != null)
{
statement.close();
}
if(connection != null)
{
connection.close();
}
}
catch(SQLException e)
{
// connection close failed.
System.err.println(e);
}
}
}
}
Vous pouvez également créer des fonctions définies par l'utilisateur. En outre, le code de test suivant sera utile pour les opérations spécifiques à SQLite. https://github.com/xerial/sqlite-jdbc/tree/c7c5604bcc584460268abc9a64df2953fca788d3/src/test/java/org/sqlite
PlantUML Vous pouvez écrire la figure suivante incluant UML dans un langage appelé DSL.
Vous pouvez officiellement essayer la description réelle de la page Web. Il existe également un plug-in Redmine, vous pouvez donc intégrer diverses figures sous forme de données texte dans le Wiki.
officiel: http://plantuml.com/ja/
Télécharger http://plantuml.com/ja/download
PlantUML Cheat Sheet https://qiita.com/ogomr/items/0b5c4de7f38fd1482a48
Vous pouvez également générer des fichiers PNG et SVG à partir de votre propre programme en incorporant le fichier plantuml.jar dans votre projet Java. http://plantuml.com/ja/api
https://github.com/mima3/BcelToSqlite
Le code ci-dessous spécifie un fichier Jar, analyse le fichier de classe qui y est stocké et enregistre les informations de classe, de méthode et de champ dans SQLite.
BcelToSqlite.java
package bcelToSqlite;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import org.apache.bcel.Const;
import org.apache.bcel.classfile.AnnotationEntry;
import org.apache.bcel.classfile.ClassFormatException;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.Constant;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.Field;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Utility;
import org.apache.bcel.generic.Type;
import org.apache.bcel.util.ByteSequence;
public class BcelToSqlite {
Connection connection = null;
PreparedStatement pstmt = null;
int nextClassId = 1;
int nextMethodId = 0x10000001;
int nextAnnotationId = 0x20000001;
/**
*Mis en œuvre en référence à ce qui suit
* https://hondou.homedns.org/pukiwiki/index.php?JavaSE%20BCEL
* @param args
* @throws Exception
*/
public static void main(String args[]) throws Exception
{
String srcPath;
srcPath = ".\\lib\\bcel-6.3.1.jar";
BcelToSqlite thisClass = new BcelToSqlite();
thisClass.startWalk(new File(srcPath));
System.out.println(srcPath + " -> output.sqlite");
}
private void executeSql(String sql) throws SQLException {
pstmt = connection.prepareStatement(sql);
pstmt.executeUpdate();
pstmt.close();
pstmt = null;
}
private void executeSql(String sql, Object args[]) throws SQLException {
pstmt = connection.prepareStatement(sql);
int ix = 1;
for (Object obj : args) {
try {
pstmt.setInt(ix, (int)Integer.parseInt(obj.toString()));
} catch (NumberFormatException ex) {
pstmt.setString(ix, obj.toString());
}
++ix;
}
pstmt.executeUpdate();
pstmt.close();
pstmt = null;
}
private void startWalk(File path) throws Exception {
try {
connection = DriverManager.getConnection("jdbc:sqlite:output.sqlite");
connection.setAutoCommit(true);
executeSql("drop table if exists class");
executeSql("create table class (id int primary key, name string , access_flags int, super_class_name string)");
executeSql("drop table if exists interface");
executeSql("create table interface (class_id int, interface_name string)");
executeSql("drop index if exists index_interface");
executeSql("create index index_interface on interface(class_id)");
executeSql("drop table if exists method");
executeSql("create table method (id int primary key, class_id int, name string, fullname string, access_flag int, return_type string, byte_code string)");
executeSql("drop table if exists method_parameter");
executeSql("create table method_parameter (method_id int, seq int, param_type string)");
executeSql("drop index if exists index_method_parameter");
executeSql("create index index_method_parameter on method_parameter(method_id)");
executeSql("drop table if exists method_depend");
executeSql("create table method_depend (method_id int, called_method string, opecode int)");
executeSql("drop index if exists index_method_depend");
executeSql("create index index_method_depend on method_depend(method_id)");
executeSql("drop table if exists field");
executeSql("create table field (id int primary key, class_id int, name string, fullname string, access_flag int, type string)");
executeSql("drop table if exists anotation");
executeSql("create table anotation (id int primary key, refid int, type string)");
executeSql("drop index if exists index_anotation");
executeSql("create index index_anotation on anotation(refid)");
connection.setAutoCommit(false);
dirWalk(path);
connection.commit();
}
finally
{
if(pstmt != null)
{
pstmt.close();
}
if(connection != null)
{
connection.close();
}
}
}
private void dirWalk(File path) throws Exception {
if (path.isDirectory()) {
for (File child : path.listFiles()) {
dirWalk(child);
}
} else if (path.getName().endsWith("jar") || path.getName().endsWith("zip")) {
jarWalk(path);
} else if (path.getName().endsWith("class")) {
JavaClass javaClass = new ClassParser(path.getAbsolutePath()).parse();
classWalk(javaClass);
}
}
private void jarWalk(File jarFile) throws Exception {
try (JarInputStream jarIn = new JarInputStream(new FileInputStream(jarFile));) {
JarEntry entry;
while ((entry = jarIn.getNextJarEntry()) != null) {
if (!entry.isDirectory()) {
String fileName = entry.getName();
if (fileName.endsWith("class")) {
JavaClass javaClass = new ClassParser(jarFile.getAbsolutePath(), fileName).parse();
classWalk(javaClass);
}
}
}
}
}
private void classWalk(final JavaClass javaClass) throws SQLException, ClassNotFoundException, IOException {
System.out.println(javaClass.getClassName());
executeSql(
"insert into class values(?, ?, ?, ?)",
new Object[] {
nextClassId,
javaClass.getClassName(),
javaClass.getAccessFlags(),
javaClass.getSuperclassName()
}
);
//Get méthode
final org.apache.bcel.classfile.Method[] methods = javaClass.getMethods();
for (org.apache.bcel.classfile.Method method : methods) {
methodWalk(nextClassId, javaClass, method);
}
Field[] fields = javaClass.getFields();
for (Field field : fields) {
fieldWalk(nextClassId, javaClass, field);
}
//Obtenir l'interface
for (JavaClass i : javaClass.getAllInterfaces()) {
if (i.getClassName().equals(javaClass.getClassName())) {
continue;
}
executeSql(
"insert into interface values(?, ?)",
new Object[] {
nextClassId,
i.getClassName()
}
);
}
//Annotation
anotationWalk(nextClassId, javaClass.getAnnotationEntries());
if (nextClassId % 500 == 0) {
connection.commit();
}
//commettre
++nextClassId;
}
private void anotationWalk(final int refId, final AnnotationEntry[] annotations) throws SQLException {
for (AnnotationEntry a : annotations) {
executeSql(
"insert into anotation values(?, ?, ?)",
new Object[] {
nextAnnotationId,
refId,
a.getAnnotationType()
}
);
}
++nextAnnotationId;
}
private void methodWalk(final int classId, final JavaClass javaClass, final org.apache.bcel.classfile.Method method) throws SQLException, IOException {
String code = getCode(method);
executeSql(
"insert into method values(?, ?, ?, ?, ?, ?, ?)",
new Object[] {
nextMethodId,
classId,
method.getName(),
javaClass.getClassName() + "." + method.getName() + " " + method.getSignature(),
method.getAccessFlags(),
method.getReturnType().toString(),
code
}
);
int seq = 1;
for(Type p : method.getArgumentTypes()) {
executeSql(
"insert into method_parameter values(?, ?, ?)",
new Object[] {
nextMethodId,
seq,
p.toString()
}
);
++seq;
}
if (method.getCode() != null) {
ByteSequence stream = new ByteSequence(method.getCode().getCode());
for (int i = 0; stream.available() > 0 ; i++) {
analyzeCode(nextMethodId, stream, method.getConstantPool());
}
}
//Annotation
anotationWalk(nextMethodId, method.getAnnotationEntries());
++nextMethodId;
}
private void fieldWalk(final int classId, final JavaClass javaClass, final org.apache.bcel.classfile.Field field) throws SQLException, IOException {
executeSql(
"insert into field values(?, ?, ?, ?, ?, ?)",
new Object[] {
nextMethodId,
classId,
field.getName(),
javaClass.getClassName() + "." + field.getName() + " " + field.getSignature(),
field.getAccessFlags(),
field.getType().toString()
}
);
//Annotation
anotationWalk(nextMethodId, field.getAnnotationEntries());
++nextMethodId;
}
private boolean wide = false; /* The `WIDE' instruction is used in the
* byte code to allow 16-bit wide indices
* for local variables. This opcode
* precedes an `ILOAD', e.g.. The opcode
* immediately following takes an extra
* byte which is combined with the
* following byte to form a
* 16-bit value.
*/
/**
*Mis en œuvre pour référence ci-dessous
* commons-bcel/src/main/java/org/apache/bcel/classfile/Utility.java
* codeToString
* @param bytes
* @param constant_pool
* @throws IOException
* @throws SQLException
* @throws ClassFormatException
*/
public void analyzeCode(final int methodId, final ByteSequence bytes, final ConstantPool constant_pool) throws IOException, ClassFormatException, SQLException {
final short opcode = (short) bytes.readUnsignedByte();
int default_offset = 0;
int low;
int high;
int npairs;
int index;
int vindex;
int constant;
int[] match;
int[] jump_table;
int no_pad_bytes = 0;
int offset;
final boolean verbose = true;
final StringBuilder buf = new StringBuilder(Const.getOpcodeName(opcode));
/* Special case: Skip (0-3) padding bytes, i.e., the
* following bytes are 4-byte-aligned
*/
if ((opcode == Const.TABLESWITCH) || (opcode == Const.LOOKUPSWITCH)) {
final int remainder = bytes.getIndex() % 4;
no_pad_bytes = (remainder == 0) ? 0 : 4 - remainder;
for (int i = 0; i < no_pad_bytes; i++) {
byte b;
if ((b = bytes.readByte()) != 0) {
System.err.println("Warning: Padding byte != 0 in "
+ Const.getOpcodeName(opcode) + ":" + b);
}
}
// Both cases have a field default_offset in common
default_offset = bytes.readInt();
}
switch (opcode) {
/* Table switch has variable length arguments.
*/
case Const.TABLESWITCH:
low = bytes.readInt();
high = bytes.readInt();
offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
default_offset += offset;
buf.append("\tdefault = ").append(default_offset).append(", low = ").append(low)
.append(", high = ").append(high).append("(");
jump_table = new int[high - low + 1];
for (int i = 0; i < jump_table.length; i++) {
jump_table[i] = offset + bytes.readInt();
buf.append(jump_table[i]);
if (i < jump_table.length - 1) {
buf.append(", ");
}
}
buf.append(")");
break;
/* Lookup switch has variable length arguments.
*/
case Const.LOOKUPSWITCH: {
npairs = bytes.readInt();
offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
match = new int[npairs];
jump_table = new int[npairs];
default_offset += offset;
buf.append("\tdefault = ").append(default_offset).append(", npairs = ").append(
npairs).append(" (");
for (int i = 0; i < npairs; i++) {
match[i] = bytes.readInt();
jump_table[i] = offset + bytes.readInt();
buf.append("(").append(match[i]).append(", ").append(jump_table[i]).append(")");
if (i < npairs - 1) {
buf.append(", ");
}
}
buf.append(")");
}
break;
/* Two address bytes + offset from start of byte stream form the
* jump target
*/
case Const.GOTO:
case Const.IFEQ:
case Const.IFGE:
case Const.IFGT:
case Const.IFLE:
case Const.IFLT:
case Const.JSR:
case Const.IFNE:
case Const.IFNONNULL:
case Const.IFNULL:
case Const.IF_ACMPEQ:
case Const.IF_ACMPNE:
case Const.IF_ICMPEQ:
case Const.IF_ICMPGE:
case Const.IF_ICMPGT:
case Const.IF_ICMPLE:
case Const.IF_ICMPLT:
case Const.IF_ICMPNE:
buf.append("\t\t#").append((bytes.getIndex() - 1) + bytes.readShort());
break;
/* 32-bit wide jumps
*/
case Const.GOTO_W:
case Const.JSR_W:
buf.append("\t\t#").append((bytes.getIndex() - 1) + bytes.readInt());
break;
/* Index byte references local variable (register)
*/
case Const.ALOAD:
case Const.ASTORE:
case Const.DLOAD:
case Const.DSTORE:
case Const.FLOAD:
case Const.FSTORE:
case Const.ILOAD:
case Const.ISTORE:
case Const.LLOAD:
case Const.LSTORE:
case Const.RET:
if (wide) {
vindex = bytes.readUnsignedShort();
wide = false; // Clear flag
} else {
vindex = bytes.readUnsignedByte();
}
buf.append("\t\t%").append(vindex);
break;
/*
* Remember wide byte which is used to form a 16-bit address in the
* following instruction. Relies on that the method is called again with
* the following opcode.
*/
case Const.WIDE:
wide = true;
buf.append("\t(wide)");
break;
/* Array of basic type.
*/
case Const.NEWARRAY:
buf.append("\t\t<").append(Const.getTypeName(bytes.readByte())).append(">");
break;
/* Access object/class fields.
*/
case Const.GETFIELD:
case Const.GETSTATIC:
case Const.PUTFIELD:
case Const.PUTSTATIC:
index = bytes.readUnsignedShort();
buf.append("\t\t").append(
constant_pool.constantToString(index, Const.CONSTANT_Fieldref)).append(
verbose ? " (" + index + ")" : "");
executeSql(
"insert into method_depend values(?,?,?)",
new Object[] {
methodId,
constant_pool.constantToString(index, Const.CONSTANT_Fieldref),
opcode
}
);
break;
/* Operands are references to classes in constant pool
*/
case Const.NEW:
case Const.CHECKCAST:
buf.append("\t");
//$FALL-THROUGH$
case Const.INSTANCEOF:
index = bytes.readUnsignedShort();
buf.append("\t<").append(
constant_pool.constantToString(index, Const.CONSTANT_Class))
.append(">").append(verbose ? " (" + index + ")" : "");
executeSql(
"insert into method_depend values(?,?,?)",
new Object[] {
methodId,
constant_pool.constantToString(index, Const.CONSTANT_Class),
opcode
}
);
break;
/* Operands are references to methods in constant pool
*/
case Const.INVOKESPECIAL:
case Const.INVOKESTATIC:
index = bytes.readUnsignedShort();
final Constant c = constant_pool.getConstant(index);
// With Java8 operand may be either a CONSTANT_Methodref
// or a CONSTANT_InterfaceMethodref. (markro)
buf.append("\t").append(
constant_pool.constantToString(index, c.getTag()))
.append(verbose ? " (" + index + ")" : "");
executeSql(
"insert into method_depend values(?,?,?)",
new Object[] {
methodId,
constant_pool.constantToString(index, c.getTag()),
opcode
}
);
break;
case Const.INVOKEVIRTUAL:
index = bytes.readUnsignedShort();
buf.append("\t").append(
constant_pool.constantToString(index, Const.CONSTANT_Methodref))
.append(verbose ? " (" + index + ")" : "");
executeSql(
"insert into method_depend values(?,?,?)",
new Object[] {
methodId,
constant_pool.constantToString(index, Const.CONSTANT_Methodref),
opcode
}
);
break;
case Const.INVOKEINTERFACE:
index = bytes.readUnsignedShort();
final int nargs = bytes.readUnsignedByte(); // historical, redundant
buf.append("\t").append(
constant_pool
.constantToString(index, Const.CONSTANT_InterfaceMethodref))
.append(verbose ? " (" + index + ")\t" : "").append(nargs).append("\t")
.append(bytes.readUnsignedByte()); // Last byte is a reserved space
executeSql(
"insert into method_depend values(?,?,?)",
new Object[] {
methodId,
constant_pool.constantToString(index, Const.CONSTANT_InterfaceMethodref),
opcode
}
);
break;
case Const.INVOKEDYNAMIC:
index = bytes.readUnsignedShort();
buf.append("\t").append(
constant_pool
.constantToString(index, Const.CONSTANT_InvokeDynamic))
.append(verbose ? " (" + index + ")\t" : "")
.append(bytes.readUnsignedByte()) // Thrid byte is a reserved space
.append(bytes.readUnsignedByte()); // Last byte is a reserved space
executeSql(
"insert into method_depend values(?,?,?)",
new Object[] {
methodId,
constant_pool.constantToString(index, Const.CONSTANT_InvokeDynamic),
opcode
}
);
break;
/* Operands are references to items in constant pool
*/
case Const.LDC_W:
case Const.LDC2_W:
index = bytes.readUnsignedShort();
buf.append("\t\t").append(
constant_pool.constantToString(index, constant_pool.getConstant(index)
.getTag())).append(verbose ? " (" + index + ")" : "");
break;
case Const.LDC:
index = bytes.readUnsignedByte();
buf.append("\t\t").append(
constant_pool.constantToString(index, constant_pool.getConstant(index)
.getTag())).append(verbose ? " (" + index + ")" : "");
break;
/* Array of references.
*/
case Const.ANEWARRAY:
index = bytes.readUnsignedShort();
buf.append("\t\t<").append(
Utility.compactClassName(constant_pool.getConstantString(index,
Const.CONSTANT_Class), false)).append(">").append(
verbose ? " (" + index + ")" : "");
break;
/* Multidimensional array of references.
*/
case Const.MULTIANEWARRAY: {
index = bytes.readUnsignedShort();
final int dimensions = bytes.readUnsignedByte();
buf.append("\t<").append(
Utility.compactClassName(constant_pool.getConstantString(index,
Const.CONSTANT_Class), false)).append(">\t").append(dimensions)
.append(verbose ? " (" + index + ")" : "");
}
break;
/* Increment local variable.
*/
case Const.IINC:
if (wide) {
vindex = bytes.readUnsignedShort();
constant = bytes.readShort();
wide = false;
} else {
vindex = bytes.readUnsignedByte();
constant = bytes.readByte();
}
buf.append("\t\t%").append(vindex).append("\t").append(constant);
break;
default:
if (Const.getNoOfOperands(opcode) > 0) {
for (int i = 0; i < Const.getOperandTypeCount(opcode); i++) {
buf.append("\t\t");
switch (Const.getOperandType(opcode, i)) {
case Const.T_BYTE:
buf.append(bytes.readByte());
break;
case Const.T_SHORT:
buf.append(bytes.readShort());
break;
case Const.T_INT:
buf.append(bytes.readInt());
break;
default: // Never reached
throw new IllegalStateException("Unreachable default case reached!");
}
}
}
}
}
private String getCode(org.apache.bcel.classfile.Method method) {
if (method.getCode() == null) {
return "";
}
return method.getCode().toString();
}
}
Un diagramme de classes est créé à partir de Sqlite qui stocke le résultat de l'analyse de bcel.
SqliteToGraph.java
package sqliteToGraph;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.apache.bcel.Const;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.SourceStringReader;
public class SqliteToGraph {
static final int MAX_MEMBER_SIZE = 10;
public class ClassData {
private int id;
private String name;
private String packageName;
private String className;
private int accessFlg;
private String superClassName;
public ClassData(ResultSet rs) throws SQLException {
id = rs.getInt("id");
name = rs.getString("name");
accessFlg = rs.getInt("access_flags");
superClassName = rs.getString("super_class_name");
int ix = name.lastIndexOf(".");
className = name.substring(ix + 1);
packageName = name.substring(0, ix);
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getPackageName() {
return packageName;
}
public String getClassName() {
return className;
}
public int getAccessFlg() {
return accessFlg;
}
public String getSuperClassName() {
return superClassName;
}
}
public static void main(String[] args) throws SQLException, IOException{
SqliteToGraph sg = new SqliteToGraph();
String dbPath = "..\\bcelToSqlite\\output.sqlite";
String path = "test_class.svg";
sg.parse(dbPath, path);
System.out.println("class:" + dbPath + "->" + path);
}
public void parse(String dbPath, String path) throws SQLException, IOException {
Connection connection = null;
ResultSet rs = null;
PreparedStatement pstmt = null;
PreparedStatement pstmtMethod = null;
StringBuilder sb = new StringBuilder();
try
{
// create a database connection
// Connection connection = DriverManager.getConnection("jdbc:sqlite:C:/work/mydatabase.db");
// Connection connection = DriverManager.getConnection("jdbc:sqlite::memory:");
connection = DriverManager.getConnection("jdbc:sqlite:" + dbPath);
pstmt = connection.prepareStatement("select id, name, access_flags, super_class_name from class order by id");
rs = pstmt.executeQuery();
HashMap<Integer, ClassData> mapClass = new HashMap<Integer, ClassData>();
while(rs.next())
{
SqliteToGraph.ClassData data = new SqliteToGraph.ClassData(rs);
mapClass.put(data.id, data);
}
rs.close();
pstmt.close();
pstmt = null;
////
sb.append("@startuml\n");
sb.append("left to right direction\n");
pstmt = connection.prepareStatement("select id, name , access_flag, type from field where class_id = ?");
pstmtMethod = connection.prepareStatement("select distinct name , access_flag from method where class_id = ?");
for(Integer key : mapClass.keySet()) {
String prefix = "class";
if ((mapClass.get(key).getAccessFlg() & Const.ACC_INTERFACE) == Const.ACC_INTERFACE) {
prefix = "interface";
}
sb.append(" " + prefix +" \"" + mapClass.get(key).name + "\" {" + "\n");
// field
List<String> list = new ArrayList<String>();
pstmt.setInt(1, mapClass.get(key).id);
rs = pstmt.executeQuery();
while(rs.next())
{
list.add(rs.getString("name"));
}
for(int i = 0; i < list.size() ; ++i) {
sb.append(" " + list.get(i) + " \n");
if (i > MAX_MEMBER_SIZE) {
sb.append(" ... \n");
break;
}
}
rs.close();
list = new ArrayList<String>();
// method
pstmtMethod.setInt(1, mapClass.get(key).id);
rs = pstmtMethod.executeQuery();
while(rs.next())
{
if ((rs.getInt("access_flag") & Const.ACC_PUBLIC) == Const.ACC_PUBLIC) {
list.add(rs.getString("name"));
}
}
rs.close();
for(int i = 0; i < list.size() ; ++i) {
sb.append(" " + list.get(i) + "() \n");
if (i > MAX_MEMBER_SIZE) {
sb.append(" ...() \n");
break;
}
}
sb.append(" }\n");
if (checkSuperClassName(mapClass.get(key).getSuperClassName())) {
sb.append(" " + mapClass.get(key).getSuperClassName() + " <|-- " + mapClass.get(key).name + "\n");
}
}
pstmt.close();
pstmt = null;
pstmt = connection.prepareStatement("select class_id, interface_name from interface");
rs = pstmt.executeQuery();
while(rs.next())
{
if (checkSuperClassName(rs.getString("interface_name"))) {
sb.append(" " + rs.getString("interface_name") + " <|.. " + mapClass.get(rs.getInt("class_id")).name + "\n");
}
}
pstmt.close();
pstmt = null;
sb.append("@enduml\n");
writeSvg(sb.toString(), path);
}
finally
{
try
{
if(rs != null)
{
rs.close();
}
if(pstmt != null)
{
pstmt.close();
}
if(pstmtMethod != null)
{
pstmtMethod.close();
}
if(connection != null)
{
connection.close();
}
}
catch(SQLException e)
{
// connection close failed.
System.err.println(e);
}
}
}
private boolean checkSuperClassName(String superClassName) {
if (superClassName.startsWith("java.")) {
return false;
}
if (superClassName.startsWith("javax.")) {
return false;
}
return true;
}
private static void writeSvg(String source, String path) throws IOException {
SourceStringReader reader = new SourceStringReader(source);
final ByteArrayOutputStream os = new ByteArrayOutputStream();
// Write the first image to "os"
@SuppressWarnings("deprecation")
String desc = reader.generateImage(os, new FileFormatOption(FileFormat.SVG));
os.close();
final String svg = new String(os.toByteArray(), Charset.forName("UTF-8"));
File out = new File(path);
PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(out)));
pw.write(svg);
pw.close();
}
}
Un graphique d'appel de la méthode spécifiée est créé à partir de Sqlite qui stocke le résultat de l'analyse de bcel.
DependMethod.java
package sqliteToGraph;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.SourceStringReader;
public class DependMethod {
public static void main(String[] args) throws SQLException, IOException{
DependMethod dm = new DependMethod();
String dbPath = "..\\bcelToSqlite\\output.sqlite";
String path = "test_depend.svg";
String methodName = "org.apache.bcel.util.ClassPath.SYSTEM_CLASS_PATH";
dm.parse(dbPath, path, methodName);
System.out.println("depend:" + dbPath + "->" + path);
}
Connection connection = null;
PreparedStatement pstmtLike = null;
PreparedStatement pstmtEqual = null;
class TreeItem {
private String methodName;
private List<TreeItem> children = new ArrayList<TreeItem>();
public TreeItem(String m) {
methodName = m;
}
public List<TreeItem> GetChildren() {
return children;
}
}
List<TreeItem> root = new ArrayList<TreeItem>();
HashMap<String, TreeItem> map = new HashMap<String, TreeItem>();
List<String> rectangles = new ArrayList<String>();
public void parse(String dbPath, String path, String methodName) throws SQLException, IOException {
ResultSet rs = null;
try
{
connection = DriverManager.getConnection("jdbc:sqlite:" + dbPath);
pstmtLike = connection.prepareStatement("select distinct method_depend.called_method from method inner join method_depend on method.id = method_depend.method_id where method_depend.called_method like ?");
pstmtLike.setString(1, "%" + methodName + "%");
rs = pstmtLike.executeQuery();
while(rs.next())
{
TreeItem item = new TreeItem(rs.getString("called_method"));
root.add(item);
map.put(item.methodName, item);
}
rs.close();
pstmtEqual = connection.prepareStatement("select distinct method.fullname as call_method from method inner join method_depend on method.id = method_depend.method_id where method_depend.called_method like ?");
for (TreeItem item : root) {
walkDependency(item.methodName);
}
StringBuilder sbDef = new StringBuilder();
StringBuilder sbArrow = new StringBuilder();
StringBuilder sb = new StringBuilder();
rectangles = new ArrayList<String>();
sb.append("@startuml\n");
for (TreeItem item : root) {
drawDependency(item, sbDef, sbArrow);
}
sb.append(sbDef.toString());
sb.append(sbArrow.toString());
sb.append("@enduml\n");
System.out.println(sb.toString());
writeSvg(sb.toString() + "\n" + sb.toString(), path);
}
finally
{
try
{
if(rs != null)
{
rs.close();
}
if(pstmtLike != null)
{
pstmtLike.close();
}
if(pstmtEqual != null)
{
pstmtEqual.close();
}
if(connection != null)
{
connection.close();
}
}
catch(SQLException e)
{
// connection close failed.
System.err.println(e);
}
}
}
void walkDependency(String calledMethod) throws SQLException {
pstmtEqual.setString(1, calledMethod);
ResultSet rs = pstmtEqual.executeQuery();
List<String> list = new ArrayList<String>();
while(rs.next())
{
String callMethod = rs.getString("call_method");
if (callMethod.equals(calledMethod)) {
//Contre-mesures contre les appels de rappel
continue;
}
list.add(callMethod);
if (!map.containsKey(callMethod)) {
TreeItem item = new TreeItem(callMethod);
map.put(item.methodName, item);
map.get(calledMethod).GetChildren().add(item);
}
}
rs.close();
for (String callMethod : list) {
walkDependency(callMethod);
}
}
void drawDependency(TreeItem item, StringBuilder sbDef, StringBuilder sbArrow) {
if (!rectangles.contains(item.methodName)) {
rectangles.add(item.methodName);
sbDef.append("rectangle \"" + item.methodName + "\" as " + makeAlias(item.methodName) + "\n");
}
for (TreeItem child : item.GetChildren()) {
sbArrow.append(makeAlias(item.methodName) + "<--" + makeAlias(child.methodName) + "\n");
drawDependency(child, sbDef, sbArrow);
}
}
private String makeAlias(String name) {
name = name.replaceAll("/", "_");
name = name.replaceAll(" ", "_");
name = name.replaceAll("<", "_");
name = name.replaceAll(">", "_");
name = name.replaceAll("\\$", "_");
name = name.replaceAll(";", "_");
name = name.replaceAll("\\(", "_");
name = name.replaceAll("\\)", "_");
name = name.replaceAll("\\[", "_");
name = name.replaceAll("\\]", "_");
return name;
}
private static void writeSvg(String source, String path) throws IOException {
SourceStringReader reader = new SourceStringReader(source);
final ByteArrayOutputStream os = new ByteArrayOutputStream();
// Write the first image to "os"
@SuppressWarnings("deprecation")
String desc = reader.generateImage(os, new FileFormatOption(FileFormat.SVG));
os.close();
final String svg = new String(os.toByteArray(), Charset.forName("UTF-8"));
File out = new File(path);
PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(out)));
pw.write(svg);
pw.close();
}
}
Avec Doxygen et GraphViz, vous n'avez pas à faire cela. Doxygen peut également cracher du XML des résultats, donc je pense que c'est facile à analyser.
Cependant, étant donné que bcel est inclus dans des outils couramment utilisés tels que findbug, il peut être utilisé même lorsque vous appréciez l'interdiction d'Internet. À ce moment-là, je le faisais en le crachant dans du texte au lieu de Sqlite, puis en le collant dans Access.
De plus, le stockage des résultats de l'analyse dans la base de données est utile pour étudier les dépendances.
Recommended Posts