@@ -84,7 +84,8 @@ private Map<String, String> getMessagesPerType(
8484 private void parseBugInstance (
8585 final XMLStreamReader xmlr ,
8686 final Set <Violation > violations ,
87- final Map <String , String > messagesPerType )
87+ final Map <String , String > messagesPerType ,
88+ final List <String > srcDirs )
8889 throws XMLStreamException {
8990 final String type = getAttribute (xmlr , "type" );
9091 final Integer rank = getIntegerAttribute (xmlr , "rank" );
@@ -105,7 +106,8 @@ private void parseBugInstance(
105106 if (!startLine .isPresent () || !endLine .isPresent ()) {
106107 continue ;
107108 }
108- final String filename = getAttribute (xmlr , "sourcepath" );
109+ final String sourcepath = getAttribute (xmlr , "sourcepath" );
110+ final String filename = resolveFilePath (sourcepath , srcDirs );
109111 final String classname = getAttribute (xmlr , "classname" );
110112 candidates .add ( //
111113 violationBuilder () //
@@ -152,20 +154,50 @@ public Set<Violation> parseReportOutput(
152154 violationsLogger ,
153155 this .getMessagesXml (findSecurityBugsMessagesXml , "/findbugs/fsb-messages.xml" )));
154156
157+ final List <String > srcDirs = new ArrayList <>();
158+
155159 try (InputStream input = new ByteArrayInputStream (string .getBytes (StandardCharsets .UTF_8 ))) {
156160 final XMLStreamReader xmlr = ViolationParserUtils .createXmlReader (input );
161+ boolean inProject = false ;
157162 while (xmlr .hasNext ()) {
158163 final int eventType = xmlr .next ();
159164 if (eventType == XMLStreamConstants .START_ELEMENT ) {
160- if (xmlr .getLocalName ().equalsIgnoreCase ("BugInstance" )) {
161- this .parseBugInstance (xmlr , violations , messagesPerType );
165+ if (xmlr .getLocalName ().equalsIgnoreCase ("Project" )) {
166+ inProject = true ;
167+ } else if (inProject && xmlr .getLocalName ().equalsIgnoreCase ("SrcDir" )) {
168+ srcDirs .add (xmlr .getElementText ());
169+ } else if (xmlr .getLocalName ().equalsIgnoreCase ("BugInstance" )) {
170+ this .parseBugInstance (xmlr , violations , messagesPerType , srcDirs );
171+ }
172+ }
173+ if (eventType == XMLStreamConstants .END_ELEMENT ) {
174+ if (xmlr .getLocalName ().equalsIgnoreCase ("Project" )) {
175+ inProject = false ;
162176 }
163177 }
164178 }
165179 }
166180 return violations ;
167181 }
168182
183+ /**
184+ * Resolves the complete file path by combining the single {@code SrcDir} with the {@code
185+ * sourcepath}. Only applies if there is exactly one {@code SrcDir} entry in total and it is a
186+ * directory (i.e. not ending with {@code .java}); otherwise returns {@code sourcepath} unchanged.
187+ */
188+ private String resolveFilePath (final String sourcepath , final List <String > srcDirs ) {
189+ if (sourcepath == null || sourcepath .isEmpty () || srcDirs .size () != 1 ) {
190+ return sourcepath ;
191+ }
192+ final String normalized = srcDirs .get (0 ).replace ("\\ " , "/" ).trim ();
193+ if (normalized .endsWith (".java" )) {
194+ return sourcepath ;
195+ }
196+ final String trimmed =
197+ normalized .endsWith ("/" ) ? normalized .substring (0 , normalized .length () - 1 ) : normalized ;
198+ return trimmed + "/" + sourcepath ;
199+ }
200+
169201 private String getMessagesXml (final String staticValue , final String messagesResourceFilename )
170202 throws IOException {
171203 String messagesXml ;
0 commit comments