Created
March 7, 2023 13:41
-
-
Save naosim/0cd857d85272f1dda996cdd3d10a01bc to your computer and use it in GitHub Desktop.
Javaのファイルを漁ってクラス構成を分析する
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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