github.com/ismailbayram/bigpicture@v0.0.0-20231225173155-e4b21f5efcff/internal/browser/java.go (about)

     1  package browser
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/ismailbayram/bigpicture/internal/graph"
     6  	"os"
     7  	"regexp"
     8  	"strings"
     9  )
    10  
    11  type JavaBrowser struct {
    12  	ignoredPaths []string
    13  	moduleName   string
    14  	rootDir      string
    15  	tree         *graph.Tree
    16  }
    17  
    18  func (b *JavaBrowser) Browse(parentPath string) {
    19  	b.moduleName = b.getModuleName()
    20  	b.browse(parentPath, b.tree.Root)
    21  	b.clearNonProjectImports()
    22  }
    23  
    24  func (b *JavaBrowser) getModuleName() string {
    25  	directory, err := os.Getwd()
    26  	if err != nil {
    27  		panic(err)
    28  	}
    29  	return strings.Split(directory, "/")[len(strings.Split(directory, "/"))-1]
    30  }
    31  
    32  func (b *JavaBrowser) clearNonProjectImports() {
    33  	for _, node := range b.tree.Nodes {
    34  		var clearedImports []string
    35  		for _, imp := range node.ImportRaw {
    36  			if _, ok := b.tree.Nodes[imp]; ok {
    37  				clearedImports = append(clearedImports, imp)
    38  			}
    39  		}
    40  		node.ImportRaw = clearedImports
    41  	}
    42  }
    43  
    44  func (b *JavaBrowser) browse(parentPath string, parentNode *graph.Node) {
    45  	entries, err := os.ReadDir(parentPath)
    46  
    47  	if err != nil {
    48  		panic(err)
    49  	}
    50  
    51  	for _, e := range entries {
    52  		fName := e.Name()
    53  		path := fmt.Sprintf("%s/%s", parentPath, fName)
    54  		if isIgnored(b.ignoredPaths, path) {
    55  			continue
    56  		}
    57  
    58  		if e.IsDir() && !strings.Contains(fName, ".") {
    59  			node := graph.NewNode(fName, path, graph.Dir, nil)
    60  			b.tree.Nodes[node.Path] = node
    61  			b.browse(path, node)
    62  			parentNode.LineCount += node.LineCount
    63  		} else if strings.HasSuffix(fName, ".java") {
    64  			node := b.parseFile(path, parentNode)
    65  			b.tree.Nodes[node.Path] = node
    66  			parentNode.LineCount += node.LineCount
    67  		}
    68  	}
    69  }
    70  
    71  func (b *JavaBrowser) parseFile(path string, parentNode *graph.Node) *graph.Node {
    72  	file, err := os.ReadFile(path)
    73  	if err != nil {
    74  		panic(err)
    75  	}
    76  	fileContent := string(file)
    77  
    78  	// extract functions
    79  	functions := make([]graph.Function, 0)
    80  	functionsInfo := b.findFunctions(fileContent)
    81  	for name, functionBody := range functionsInfo {
    82  		functions = append(functions, graph.Function{
    83  			Name:      name,
    84  			LineCount: len(strings.Split(functionBody, "\n")) - 1,
    85  		})
    86  	}
    87  
    88  	fName := path[strings.LastIndex(path, "/")+1:]
    89  	node := graph.NewNode(fName, path, graph.File, b.findImports(fileContent))
    90  	node.Functions = functions
    91  	node.LineCount = strings.Count(fileContent, "\n")
    92  
    93  	return node
    94  }
    95  
    96  func (b *JavaBrowser) findImports(javaCode string) []string {
    97  	var imports []string
    98  
    99  	importPattern := regexp.MustCompile(`import\s+([^;\n]+);`)
   100  
   101  	matches := importPattern.FindAllSubmatch([]byte(javaCode), -1)
   102  	for _, match := range matches {
   103  		importItem := string(match[1])
   104  		if strings.HasPrefix(importItem, "java.") {
   105  			continue
   106  		}
   107  
   108  		importItem = b.rootDir + strings.Replace(importItem, ".", "/", -1) + ".java"
   109  		if strings.HasSuffix(importItem, "*.java") {
   110  			importItem = importItem[:len(importItem)-7]
   111  		}
   112  
   113  		imports = append(imports, importItem)
   114  	}
   115  
   116  	return imports
   117  }
   118  
   119  func (b *JavaBrowser) findFunctions(fileContent string) map[string]string {
   120  	functions := make(map[string]string)
   121  
   122  	functionPattern := regexp.MustCompile(`(?:public|private|protected)?\s+(?:static\s+)?\w+\s+([\w<>]+)\s+(\w+)\s*\([^)]*\)\s*(?:throws\s+\w+(?:\s*,\s*\w+)*)?\s*\{`)
   123  	matches := functionPattern.FindAllStringSubmatch(fileContent, -1)
   124  
   125  	for _, match := range matches {
   126  		startIndex := strings.Index(fileContent, match[0])
   127  		endIndex := findFunctionEndIndex(fileContent, startIndex)
   128  		functionContent := fileContent[startIndex+len(match[0]) : endIndex]
   129  		functions[match[2]] = functionContent
   130  	}
   131  
   132  	return functions
   133  }
   134  
   135  func findFunctionEndIndex(javaCode string, startIndex int) int {
   136  	openBraces := 0
   137  	for i := startIndex + 1; i < len(javaCode); i++ {
   138  		if javaCode[i] == '{' {
   139  			openBraces++
   140  		} else if javaCode[i] == '}' {
   141  			openBraces--
   142  			if openBraces == 0 {
   143  				return i - 2
   144  			}
   145  		}
   146  	}
   147  	return -1
   148  }