2020
2121import java .io .IOException ;
2222import java .io .InputStream ;
23- import java .util .Collections ;
2423import java .util .HashMap ;
2524import java .util .HashSet ;
26- import java .util .List ;
2725import java .util .Map ;
2826import java .util .Optional ;
2927import java .util .Set ;
@@ -89,50 +87,83 @@ private Map<PackageRef, DirectDependency> buildDependencies(
8987 return buildUnknownDependencies (componentPurls );
9088 }
9189
92- Map <PackageRef , List <PackageRef >> dependencies =
93- bom .getDependencies (). stream ()
94- . collect (
95- Collectors . toMap (
96- d -> {
97- if ( componentPurls . get ( d . getRef ()) == null ) {
98- return rootRef ;
99- }
100- return componentPurls . get ( d . getRef ());
101- },
102- d -> {
103- if (d . getDependencies () = = null ) {
104- return Collections . emptyList ( );
105- }
106- return d . getDependencies (). stream ()
107- . map ( dep -> componentPurls . get ( dep . getRef ()))
108- . toList ( );
109- }) );
110- List < PackageRef > directDeps ;
90+ Map <PackageRef , Set <PackageRef >> dependencies = new HashMap <>();
91+ bom .getDependencies ()
92+ . forEach (
93+ d -> {
94+ PackageRef ref = componentPurls . getOrDefault ( d . getRef (), rootRef );
95+ Set < PackageRef > deps = new HashSet <>();
96+ if ( d . getDependencies () != null ) {
97+ d . getDependencies ()
98+ . forEach (
99+ dep -> {
100+ PackageRef depRef = componentPurls . get ( dep . getRef ());
101+ if (depRef ! = null ) {
102+ deps . add ( depRef );
103+ }
104+ });
105+ }
106+ dependencies . put ( ref , deps );
107+ } );
108+
111109 addUnknownDependencies (dependencies , componentPurls );
112- if (rootRef != null && dependencies .get (rootRef ) != null ) {
113- directDeps = dependencies .get (rootRef );
110+
111+ Set <PackageRef > directDeps ;
112+ if (rootRef != null && dependencies .containsKey (rootRef )) {
113+ directDeps = new HashSet <>(dependencies .get (rootRef ));
114114 } else {
115- directDeps =
116- dependencies .keySet ().stream ()
117- .filter (depRef -> dependencies .values ().stream ().noneMatch (d -> d .contains (depRef )))
118- .toList ();
115+ directDeps = new HashSet <>(dependencies .keySet ());
116+ dependencies .values ().forEach (directDeps ::removeAll );
119117 }
120118
121- return directDeps .stream ()
122- .map (directRef -> toDirectDependency (directRef , dependencies ))
123- .collect (Collectors .toMap (DirectDependency ::ref , d -> d ));
119+ componentPurls .values ().stream ()
120+ .filter (Predicate .not (dependencies ::containsKey ))
121+ .forEach (directDeps ::add );
122+
123+ Map <PackageRef , DirectDependency > result = new HashMap <>();
124+ directDeps .forEach (
125+ directRef -> {
126+ Set <PackageRef > transitiveRefs = new HashSet <>();
127+ findTransitiveIterative (directRef , dependencies , transitiveRefs );
128+ result .put (
129+ directRef ,
130+ DirectDependency .builder ().ref (directRef ).transitive (transitiveRefs ).build ());
131+ });
132+
133+ return result ;
124134 }
125135
126136 private void addUnknownDependencies (
127- Map <PackageRef , List <PackageRef >> dependencies , Map <String , PackageRef > componentPurls ) {
137+ Map <PackageRef , Set <PackageRef >> dependencies , Map <String , PackageRef > componentPurls ) {
128138 Set <PackageRef > knownDeps = new HashSet <>(dependencies .keySet ());
129- dependencies .values ().forEach (v -> knownDeps . addAll ( v ) );
139+ dependencies .values ().forEach (knownDeps :: addAll );
130140 componentPurls .values ().stream ()
131141 .filter (Predicate .not (knownDeps ::contains ))
132- .forEach (d -> dependencies .put (d , Collections .emptyList ()));
142+ .forEach (d -> dependencies .put (d , new HashSet <>()));
143+ }
144+
145+ private void findTransitiveIterative (
146+ PackageRef startRef , Map <PackageRef , Set <PackageRef >> dependencies , Set <PackageRef > acc ) {
147+ Set <PackageRef > toProcess = new HashSet <>();
148+ toProcess .add (startRef );
149+
150+ while (!toProcess .isEmpty ()) {
151+ PackageRef current = toProcess .iterator ().next ();
152+ toProcess .remove (current );
153+
154+ Set <PackageRef > deps = dependencies .get (current );
155+ if (deps != null ) {
156+ deps .stream ()
157+ .filter (d -> !acc .contains (d ))
158+ .forEach (
159+ d -> {
160+ acc .add (d );
161+ toProcess .add (d );
162+ });
163+ }
164+ }
133165 }
134166
135- // The SBOM generator does not have info about the dependency hierarchy
136167 private Map <PackageRef , DirectDependency > buildUnknownDependencies (
137168 Map <String , PackageRef > componentPurls ) {
138169 Map <PackageRef , DirectDependency > deps = new HashMap <>();
@@ -148,29 +179,6 @@ private Map<PackageRef, DirectDependency> buildUnknownDependencies(
148179 return deps ;
149180 }
150181
151- private DirectDependency toDirectDependency (
152- PackageRef directRef , Map <PackageRef , List <PackageRef >> dependencies ) {
153- var transitiveRefs = new HashSet <PackageRef >();
154- findTransitive (directRef , dependencies , transitiveRefs );
155- var transitive = new HashSet <>(transitiveRefs .stream ().toList ());
156- return DirectDependency .builder ().ref (directRef ).transitive (transitive ).build ();
157- }
158-
159- private void findTransitive (
160- PackageRef ref , Map <PackageRef , List <PackageRef >> dependencies , Set <PackageRef > acc ) {
161- var deps = dependencies .get (ref );
162- if (deps == null || deps .isEmpty ()) {
163- return ;
164- }
165- deps .stream ()
166- .filter (d -> !acc .contains (d ))
167- .forEach (
168- d -> {
169- acc .add (d );
170- findTransitive (d , dependencies , acc );
171- });
172- }
173-
174182 private Bom parseBom (InputStream input ) {
175183 try {
176184 JsonNode node = MAPPER .readTree (input );
@@ -196,21 +204,15 @@ private Version parseSchemaVersion(String version) throws ParseException {
196204 if (version == null ) {
197205 throw new ParseException ("Missing CycloneDX Spec Version" );
198206 }
199- switch (version ) {
200- case "1.5" :
201- return Version .VERSION_15 ;
202- case "1.4" :
203- return Version .VERSION_14 ;
204- case "1.3" :
205- return Version .VERSION_13 ;
206- case "1.2" :
207- return Version .VERSION_12 ;
208- case "1.1" :
209- return Version .VERSION_11 ;
210- case "1.0" :
211- return Version .VERSION_10 ;
212- default :
213- throw new ParseException ("Invalid Spec Version received" );
214- }
207+ return switch (version ) {
208+ case "1.6" -> Version .VERSION_16 ;
209+ case "1.5" -> Version .VERSION_15 ;
210+ case "1.4" -> Version .VERSION_14 ;
211+ case "1.3" -> Version .VERSION_13 ;
212+ case "1.2" -> Version .VERSION_12 ;
213+ case "1.1" -> Version .VERSION_11 ;
214+ case "1.0" -> Version .VERSION_10 ;
215+ default -> throw new ParseException ("Invalid Spec Version received" );
216+ };
215217 }
216218}
0 commit comments