@@ -88,47 +88,15 @@ private DetectorResult detectWithAst(CompilationUnit cu, DetectorContext ctx) {
8888 List <CodeEdge > edges = new ArrayList <>();
8989
9090 cu .findAll (ClassOrInterfaceDeclaration .class ).forEach (classDecl -> {
91- // Only process @Entity annotated classes
9291 boolean isEntity = classDecl .getAnnotations ().stream ()
9392 .anyMatch (a -> "Entity" .equals (a .getNameAsString ()));
9493 if (!isEntity ) return ;
9594
9695 String className = classDecl .getNameAsString ();
9796 String fqn = resolveFqn (cu , className );
9897 int classLine = classDecl .getBegin ().map (p -> p .line ).orElse (1 );
99-
100- // Extract table name from @Table annotation
101- String tableName = className .toLowerCase ();
102- for (AnnotationExpr ann : classDecl .getAnnotations ()) {
103- if ("Table" .equals (ann .getNameAsString ())) {
104- String name = extractAnnotationStringAttr (ann , "name" );
105- if (name == null ) {
106- // Try bare value
107- name = extractAnnotationValue (ann );
108- }
109- if (name != null ) tableName = name ;
110- }
111- }
112-
113- // Extract columns from fields
114- List <Map <String , String >> columns = new ArrayList <>();
115- for (FieldDeclaration field : classDecl .getFields ()) {
116- for (VariableDeclarator var : field .getVariables ()) {
117- String fieldName = var .getNameAsString ();
118- String fieldType = var .getTypeAsString ();
119-
120- // Check for @Column annotation
121- for (AnnotationExpr ann : field .getAnnotations ()) {
122- if ("Column" .equals (ann .getNameAsString ())) {
123- String colName = extractAnnotationStringAttr (ann , "name" );
124- if (colName == null ) colName = fieldName ;
125- columns .add (Map .of ("name" , colName , "field" , fieldName , "type" , fieldType ));
126- } else if ("Id" .equals (ann .getNameAsString ())) {
127- columns .add (Map .of ("name" , fieldName , "field" , fieldName , "type" , fieldType ));
128- }
129- }
130- }
131- }
98+ String tableName = extractTableName (classDecl , className );
99+ List <Map <String , String >> columns = extractColumns (classDecl );
132100
133101 String entityId = ctx .filePath () + ":" + className ;
134102 Map <String , Object > properties = new LinkedHashMap <>();
@@ -148,59 +116,92 @@ private DetectorResult detectWithAst(CompilationUnit cu, DetectorContext ctx) {
148116 nodes .add (node );
149117 DetectorDbHelper .addDbEdge (entityId , ctx .registry (), nodes , edges );
150118
151- // Extract relationship edges from fields
152- for (FieldDeclaration field : classDecl .getFields ()) {
153- for (AnnotationExpr ann : field .getAnnotations ()) {
154- String annName = ann .getNameAsString ();
155- if (!RELATIONSHIP_ANNOTATIONS .contains (annName )) continue ;
119+ extractRelationshipEdges (classDecl , entityId , edges );
120+ });
156121
157- String relType = RELATIONSHIP_TYPES .get (annName );
122+ return DetectorResult .of (nodes , edges );
123+ }
158124
159- // Resolve target entity
160- String targetEntity = extractAnnotationStringAttr (ann , "targetEntity" );
161- if (targetEntity != null && targetEntity .endsWith (".class" )) {
162- targetEntity = targetEntity .replace (".class" , "" );
163- }
125+ private String extractTableName (ClassOrInterfaceDeclaration classDecl , String className ) {
126+ for (AnnotationExpr ann : classDecl .getAnnotations ()) {
127+ if (!"Table" .equals (ann .getNameAsString ())) continue ;
128+ String name = extractAnnotationStringAttr (ann , "name" );
129+ if (name == null ) name = extractAnnotationValue (ann );
130+ if (name != null ) return name ;
131+ }
132+ return className .toLowerCase ();
133+ }
164134
165- if (targetEntity == null ) {
166- // Try to resolve from field type / generic type argument
167- for (VariableDeclarator var : field .getVariables ()) {
168- Type type = var .getType ();
169- if (type .isClassOrInterfaceType ()) {
170- ClassOrInterfaceType cit = type .asClassOrInterfaceType ();
171- if (cit .getTypeArguments ().isPresent ()) {
172- // Generic type like List<Order> -> Order
173- var typeArgs = cit .getTypeArguments ().get ();
174- if (!typeArgs .isEmpty ()) {
175- targetEntity = typeArgs .get (0 ).asString ();
176- }
177- } else {
178- targetEntity = cit .getNameAsString ();
179- }
180- break ;
181- }
182- }
183- }
135+ private List <Map <String , String >> extractColumns (ClassOrInterfaceDeclaration classDecl ) {
136+ List <Map <String , String >> columns = new ArrayList <>();
137+ for (FieldDeclaration field : classDecl .getFields ()) {
138+ for (VariableDeclarator var : field .getVariables ()) {
139+ addColumnFromAnnotations (field , var .getNameAsString (), var .getTypeAsString (), columns );
140+ }
141+ }
142+ return columns ;
143+ }
184144
185- if (targetEntity != null ) {
186- String mappedBy = extractAnnotationStringAttr (ann , "mappedBy" );
187- Map <String , Object > edgeProps = new LinkedHashMap <>();
188- edgeProps .put ("relationship_type" , relType );
189- if (mappedBy != null ) edgeProps .put ("mapped_by" , mappedBy );
190-
191- CodeEdge edge = new CodeEdge ();
192- edge .setId (entityId + "->maps_to->*:" + targetEntity );
193- edge .setKind (EdgeKind .MAPS_TO );
194- edge .setSourceId (entityId );
195- edge .setTarget (new CodeNode ("*:" + targetEntity , NodeKind .ENTITY , targetEntity ));
196- edge .setProperties (edgeProps );
197- edges .add (edge );
198- }
199- }
145+ private void addColumnFromAnnotations (FieldDeclaration field , String fieldName ,
146+ String fieldType , List <Map <String , String >> columns ) {
147+ for (AnnotationExpr ann : field .getAnnotations ()) {
148+ if ("Column" .equals (ann .getNameAsString ())) {
149+ String colName = extractAnnotationStringAttr (ann , "name" );
150+ if (colName == null ) colName = fieldName ;
151+ columns .add (Map .of ("name" , colName , "field" , fieldName , "type" , fieldType ));
152+ } else if ("Id" .equals (ann .getNameAsString ())) {
153+ columns .add (Map .of ("name" , fieldName , "field" , fieldName , "type" , fieldType ));
200154 }
201- });
155+ }
156+ }
202157
203- return DetectorResult .of (nodes , edges );
158+ private void extractRelationshipEdges (ClassOrInterfaceDeclaration classDecl ,
159+ String entityId , List <CodeEdge > edges ) {
160+ for (FieldDeclaration field : classDecl .getFields ()) {
161+ for (AnnotationExpr ann : field .getAnnotations ()) {
162+ String annName = ann .getNameAsString ();
163+ if (!RELATIONSHIP_ANNOTATIONS .contains (annName )) continue ;
164+
165+ String relType = RELATIONSHIP_TYPES .get (annName );
166+ String targetEntity = resolveTargetEntity (ann , field );
167+ if (targetEntity == null ) continue ;
168+
169+ String mappedBy = extractAnnotationStringAttr (ann , "mappedBy" );
170+ Map <String , Object > edgeProps = new LinkedHashMap <>();
171+ edgeProps .put ("relationship_type" , relType );
172+ if (mappedBy != null ) edgeProps .put ("mapped_by" , mappedBy );
173+
174+ CodeEdge edge = new CodeEdge ();
175+ edge .setId (entityId + "->maps_to->*:" + targetEntity );
176+ edge .setKind (EdgeKind .MAPS_TO );
177+ edge .setSourceId (entityId );
178+ edge .setTarget (new CodeNode ("*:" + targetEntity , NodeKind .ENTITY , targetEntity ));
179+ edge .setProperties (edgeProps );
180+ edges .add (edge );
181+ }
182+ }
183+ }
184+
185+ private String resolveTargetEntity (AnnotationExpr ann , FieldDeclaration field ) {
186+ String targetEntity = extractAnnotationStringAttr (ann , "targetEntity" );
187+ if (targetEntity != null && targetEntity .endsWith (".class" )) {
188+ targetEntity = targetEntity .replace (".class" , "" );
189+ }
190+ if (targetEntity != null ) return targetEntity ;
191+
192+ for (VariableDeclarator var : field .getVariables ()) {
193+ Type type = var .getType ();
194+ if (!type .isClassOrInterfaceType ()) continue ;
195+ ClassOrInterfaceType cit = type .asClassOrInterfaceType ();
196+ if (cit .getTypeArguments ().isPresent ()) {
197+ var typeArgs = cit .getTypeArguments ().get ();
198+ if (!typeArgs .isEmpty ()) return typeArgs .get (0 ).asString ();
199+ } else {
200+ return cit .getNameAsString ();
201+ }
202+ break ;
203+ }
204+ return null ;
204205 }
205206
206207 private String extractAnnotationStringAttr (AnnotationExpr ann , String attrName ) {
0 commit comments