Skip to content

Instantly share code, notes, and snippets.

@naosim
Created March 7, 2023 13:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save naosim/0cd857d85272f1dda996cdd3d10a01bc to your computer and use it in GitHub Desktop.
Save naosim/0cd857d85272f1dda996cdd3d10a01bc to your computer and use it in GitHub Desktop.
Javaのファイルを漁ってクラス構成を分析する
/*
Javaのファイルを漁ってクラス構成を分析する
---
# 実行環境 node.js
古いバージョンでも動くようにする。モダンな書き方はアロー式を使う程度にする
# 出力形式
[
{
filePath: string,
className: string,
isInterface: boolean,
isClass: boolean,
isDataSourceRepository: boolean, // DDDありのとき
imports: string[],
implements: string[],
package: string,
fullPackage: string,
domainRepositoryName?: string //DDDあり、かつ、isDataSourceRepositoryがtrueのとき
},...
]
*/
const exec = require('child_process').exec
const fs = require('fs')
// ★ 各自設定をお願いします ★
var CONFIG = {
ROOT_PATH: './', // findを開始するパス
IS_DDD: true, // ドメイン駆動設計特有の項目有無
}
/**
* javaのソースコードから情報を取り出す
* @param {{filePath: string, sourceCode: string}} obj
*/
function getJavaData(obj) {
var result = {
filePath: obj.filePath,
className: getClassName(obj.filePath),
isInterface: false,
isClass: false,
package: null,
fullPackage: null,
imports: [],
implements: [],
}
if(CONFIG.IS_DDD) {
result.isRepositoryImpl = false;
result.isDataSourceRepository = false;
result.isAppService = false;
result.isOtherComponent = false;
result.isRestController = false;
result.isBatchMessageEndpoint = false;
result.isDiComponent = false;
}
// ソースを1行ずつ解析する
obj.sourceCode.split('\n').forEach(line => {
line = line.trim();
if(line.indexOf('package') == 0) {
result.package = line.split('package')[1].split(';')[0].trim();
} else if(line.indexOf('import') == 0) {
result.imports.push(line.split('import')[1].split(';')[0].trim());
} else if(line.indexOf('interface') != -1) {
result.isInterface = true;
} else {
if(line.indexOf('class') != -1) {
result.isClass = true;
}
if(line.indexOf('implements') != -1) {
result.implements = getImplements(line);
}
if(CONFIG.IS_DDD && line.indexOf('@Repository') == 0) {
result.isDataSourceRepository = true;
result.isDiComponent = true;
}
if(CONFIG.IS_DDD && line.indexOf('@Service') == 0) {
result.isAppService = true;
result.isDiComponent = true;
}
if(CONFIG.IS_DDD && line.indexOf('@Component') == 0) {
result.isOtherComponent = true;
result.isDiComponent = true;
}
if(CONFIG.IS_DDD && line.indexOf('@RestController') == 0) {
result.isRestController = true;
result.isDiComponent = true;
}
if(CONFIG.IS_DDD && line.indexOf('@BatchMessageEndpoint') == 0) {
result.isBatchMessageEndpoint = true;
result.isDiComponent = true;
}
}
});
result.fullPackage = result.package + '.' + result.className;
if(CONFIG.IS_DDD && result.isDataSourceRepository) {
result.domainRepositoryName = detectDomainRepositoryName(result.className, result.implements);
}
return result
}
/**
* ファイル名からクラス名を取得する
* @param {string} filePath
* @returns
*/
function getClassName(filePath) {
var lastSegment = filePath.split('/').pop();
return lastSegment.split('.')[0];
}
/**
* リポジトリのクラス名からドメイン層リポジトリを推定する
*
* @param {string} dataSourceRepositoryClassName
* @param {string[]} implements
* @returns {string | null}
*/
function detectDomainRepositoryName(dataSourceRepositoryClassName, implements) {
if(implements.length == 0) {
return null;
}
if(implements.length == 1) {
return implements[0];
} else {
return dataSourceRepositoryClassName.split('Repository')[0] + 'Repository';
}
}
function getImplements(line) {
if(line.indexOf('implements') == -1) {
throw new Error('implements not found');
}
// インターフェース部分を取得する。ただしジェネリクスが含まれる
var interfaceStringWithGenerics = line.split('implements')[1].split('{')[0];
var nest = 0;
// ジェネリクスを抜く
var interfaceString = interfaceStringWithGenerics.split('').filter((v, i) => {
if(v == '<') {
nest++;
return false;
} else if(v == '>') {
nest--;
return false;
}
return nest == 0;
}).join('')
return interfaceString.split(',').map(v => v.trim())
}
// メイン処理
exec('find ' + CONFIG.ROOT_PATH + ' -name "*.java"', (err, stdout, stderr) => {
if (err) {
console.log(`stderr: ${stderr}`)
return
}
var javaDataList = stdout.trim().split('\n')
.map(v => ({filePath: v, sourceCode:fs.readFileSync(v, 'utf8')}))
.map(v => getJavaData(v));
// JSON出力
console.log(JSON.stringify(javaDataList));
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment