Javaプロジェクトでの循環参照の検出

18588 ワード

Javaプロジェクトがますます大きくなり、複数の開発者が作業しているため、コードに循環参照がある可能性があります.したがって,ループリファレンスを用いてオブジェクトをシーケンス化することは困難である.Javaプロジェクトの循環参照を検出するツールを作成しました.循環参照検出器Gitlabソースコードです.Javaプロジェクトのループ参照を検出するには、次の手順に従います.
計画目標
  • Javaプロジェクトのソースファイルのパスを入力します.
  • 出力:ループ参照の各クラスのループ図を含む画像ファイルのフォルダ.

  • ステップ
  • Java解析器を使用して、所与のソースディレクトリ内の各Javaファイルを解析し、JgraphTを使用して図に収集するクラスの参照
  • がある.
  • JgraphTのCycleDetectorを用いて図中の各頂点の周期
  • を検出する.
  • jgrapht-extライブラリを使用してPNGファイル
  • としてグラフィックを格納する.
    コードセグメント
    ソースディレクトリのJavaファイルを解析し、クラスリファレンスをグラフィックに追加します.
    Graph<String, DefaultEdge> classReferencesGraph = new DefaultDirectedGraph<>(DefaultEdge.class);
    try (Stream<Path> filesStream = Files.walk(Paths.get(srcDirectory))) {
        filesStream             
        .filter(path -> path.getFileName().toString().endsWith(".java"))
        .forEach(path ->{
            Set<String> instanceVarTypes = getInstanceVarTypes(path.toFile());
            if( ! instanceVarTypes.isEmpty()) {                     
                String className = getClassName(path.getFileName().toString());
                classReferencesGraph.addVertex(className);
                instanceVarTypes.forEach(classReferencesGraph::addVertex);
                instanceVarTypes.forEach( var -> classReferencesGraph.addEdge(className, var));
            }
        });
    }
    

     
    Java解析器を使用してクラスのインスタンス変数を収集する
    CompilationUnit compilationUnit = StaticJavaParser.parse(javaSrcFile);      
    List<Node> instanceVarTypes = compilationUnit.findAll(FieldDeclaration.class)
        .stream()
        .map(f -> f.getVariables().get(0).getType())
        .filter(v -> !v.isPrimitiveType())
        .map( Object::toString)
        .collect(Collectors.toSet());
    

     
    地図を使って有向図を作成する
    Graph<String, DefaultEdge> graph = new DefaultDirectedGraph<>(DefaultEdge.class);       
    //add vertices
    classNametoInstanceVarTypesMap.keySet().forEach(className ->{           
      graph.addVertex(className);
    });
    //add edges
    classNametoInstanceVarTypesMap
      .forEach((className , instanceVariableTypes) -> {
          instanceVariableTypes.forEach( instVar -> {
            if (classNametoInstanceVarTypesMap.containsKey(instVar)){
              graph.addEdge(className, instVar);
            }           
        });
    });
    

     
    JGraphT CycleDetectorを使用して、図の各頂点の周期を検出し、地図に周期を追加します.
    Map<String, AsSubgraph<String, DefaultEdge>> cyclesForEveryVertexMap = new HashMap<>();
    CycleDetector<String, DefaultEdge> cycleDetector = new CycleDetector<>(classReferencesGraph);
    cycleDetector.findCycles().forEach(v -> {
        AsSubgraph<String, DefaultEdge> subGraph = new AsSubgraph<>(classReferencesGraph,
                cycleDetector.findCyclesContainingVertex(v));
        cyclesForEveryVertexMap.put(v,subGraph);
    });
    

     
  • findCyclesContainingVertex(vertex)は、頂点ループに関与する頂点のセットを返す.これらの頂点を使用して、メインマップのサブマップを作成します.

  • jgrapht-extライブラリを使用して、図のPNG画像を作成します.
    new File(outputDirectoryPath).mkdirs();
    File imgFile = new File(outputDirectoryPath+"/graph" + imageName + ".png");
    if(imgFile.createNewFile()) {
        JGraphXAdapter<String, DefaultEdge> graphAdapter = new JGraphXAdapter<>(subGraph);
        mxIGraphLayout layout = new mxCircleLayout(graphAdapter);
        layout.execute(graphAdapter.getDefaultParent());
     
        BufferedImage image = mxCellRenderer.createBufferedImage(graphAdapter, null, 2, Color.WHITE, true, null);
        if (image != null) {
            ImageIO.write(image, "PNG", imgFile);
        }
    }
    

     
    サンプル出力グラフィック画像
    テキストリンク:https://dev.to//nikhilpereira1793/detecting-circular-references-in-a-java-project-32fd