3030import java .util .List ;
3131import java .util .Map ;
3232import java .util .Objects ;
33+ import java .util .Optional ;
3334import java .util .Set ;
3435import java .util .TreeMap ;
3536import java .util .TreeSet ;
3637import java .util .function .BiFunction ;
3738import java .util .function .Function ;
3839import java .util .function .Predicate ;
3940import java .util .stream .Collectors ;
41+ import java .util .stream .Stream ;
4042import javax .lang .model .element .Element ;
4143import javax .lang .model .element .ModuleElement ;
4244import javax .lang .model .element .ModuleElement .Directive ;
@@ -163,7 +165,7 @@ protected List<Content> buildEnclosedElements() {
163165 private <T > void addDirectives (List <Content > contents ,
164166 String headingKey ,
165167 Predicate <RelativePosition <?>> filter ,
166- BiFunction <RelativePosition <?>, APIMap <T >, Content > f ) {
168+ BiFunction <RelativePosition <?>, APIMap <T >, ContentAndResultKind > f ) {
167169 Map <RelativePosition <?>, APIMap <T >> dMaps = new TreeMap <>(RelativePosition .elementKeyIndexComparator );
168170 // apiMaps will only contain maps for directives which should be compared and displayed;
169171 // i.e. they have already been filtered according to accessKind.allDirectiveDetails
@@ -179,27 +181,44 @@ private <T> void addDirectives(List<Content> contents,
179181 }
180182 }
181183
182- if (!dMaps .isEmpty ()) {
184+ List <ContentAndResultKind > converted =
185+ dMaps .entrySet ()
186+ .stream ()
187+ .map (e -> f .apply (e .getKey (), e .getValue ()))
188+ .toList ();
189+
190+ if (!converted .isEmpty ()) {
191+ boolean allUnchanged = converted .stream ()
192+ .allMatch (c -> c .resultKind () == ResultKind .SAME );
183193 HtmlTree section = HtmlTree .SECTION ().setClass ("enclosed" );
184194 section .add (HtmlTree .H2 (Text .of (msgs .getString (headingKey ))));
185195 HtmlTree ul = HtmlTree .UL ();
186- dMaps .forEach ((rp , apiMap ) -> ul .add (HtmlTree .LI (f .apply (rp , apiMap ))));
196+ for (ContentAndResultKind c : converted ) {
197+ HtmlTree item = HtmlTree .LI (c .content ());
198+ if (!allUnchanged && c .resultKind () == ResultKind .SAME ) {
199+ item .setClass ("unchanged" );
200+ }
201+ ul .add (item );
202+ }
187203 section .add (ul );
204+ if (allUnchanged ) {
205+ section = HtmlTree .DIV (section ).setClass ("unchanged" );
206+ }
188207 contents .add (section );
189208 }
190209 }
191210
192- private Content buildExports (RelativePosition <?> rPos , APIMap <ExportsDirective > apiMap ) {
211+ private ContentAndResultKind buildExports (RelativePosition <?> rPos , APIMap <ExportsDirective > apiMap ) {
193212 return buildExportsOpensProvides (rPos , apiMap , Keywords .EXPORTS , Keywords .TO ,
194213 ExportsDirective ::getPackage , ExportsDirective ::getTargetModules );
195214 }
196215
197- private Content buildOpens (RelativePosition <?> rPos , APIMap <OpensDirective > apiMap ) {
216+ private ContentAndResultKind buildOpens (RelativePosition <?> rPos , APIMap <OpensDirective > apiMap ) {
198217 return buildExportsOpensProvides (rPos , apiMap , Keywords .OPENS , Keywords .TO ,
199218 OpensDirective ::getPackage , OpensDirective ::getTargetModules );
200219 }
201220
202- private Content buildProvides (RelativePosition <?> rPos , APIMap <ProvidesDirective > apiMap ) {
221+ private ContentAndResultKind buildProvides (RelativePosition <?> rPos , APIMap <ProvidesDirective > apiMap ) {
203222 // ProvidesDirective is unusual in that part of it (i.e. the implementations)
204223 // is not part of the public API, and should only be displayed if allDirectiveDetails
205224 // is true.
@@ -208,13 +227,13 @@ private Content buildProvides(RelativePosition<?> rPos, APIMap<ProvidesDirective
208227 pd -> allDirectiveDetails ? pd .getImplementations () : Collections .emptyList ());
209228 }
210229
211- private Content buildUses (RelativePosition <?> rPos , APIMap <UsesDirective > apiMap ) {
230+ private ContentAndResultKind buildUses (RelativePosition <?> rPos , APIMap <UsesDirective > apiMap ) {
212231 return buildExportsOpensProvides (rPos , apiMap , Keywords .USES , Content .empty ,
213232 UsesDirective ::getService , d -> Collections .emptyList ());
214233 }
215234
216235 private <T extends Directive , U extends Element , V extends Element >
217- Content buildExportsOpensProvides (RelativePosition <?> rPos , APIMap <T > apiMap ,
236+ ContentAndResultKind buildExportsOpensProvides (RelativePosition <?> rPos , APIMap <T > apiMap ,
218237 Content directiveKeyword , Content sublistKeyword ,
219238 Function <T , U > getPrimaryElement ,
220239 Function <T , List <V >> getSecondaryElements ) {
@@ -261,13 +280,14 @@ Content buildExportsOpensProvides(RelativePosition<?> rPos, APIMap<T> apiMap,
261280 }
262281 }
263282
264-
265283 // TODO: for now, this is stylistically similar to buildEnclosedElement,
266284 // but arguably a better way would be to move code for the check or cross into
267285 // the enclosing loop that builds the list.
268- return HtmlTree .SPAN (getResultGlyph (rPos ), Text .SPACE )
269- .add (HtmlTree .SPAN (contents ).setClass ("signature" ));
270286
287+ ResultKind result = getResultKind (rPos );
288+
289+ return new ContentAndResultKind (HtmlTree .SPAN (result .getContent (), Text .SPACE )
290+ .add (HtmlTree .SPAN (contents ).setClass ("signature" )), result );
271291 }
272292
273293 private Content getName (ElementKey ek ) {
@@ -288,7 +308,7 @@ private CharSequence getQualifiedName(ElementKey ek) {
288308 }
289309 }
290310
291- private Content buildRequires (RelativePosition <?> rPos , APIMap <RequiresDirective > apiMap ) {
311+ private ContentAndResultKind buildRequires (RelativePosition <?> rPos , APIMap <RequiresDirective > apiMap ) {
292312 List <Content > contents = new ArrayList <>();
293313
294314 contents .add (Keywords .REQUIRES );
@@ -337,11 +357,12 @@ private Content buildRequires(RelativePosition<?> rPos, APIMap<RequiresDirective
337357 // TODO: would be nice to link to the module page if it can be determined to be available.
338358 contents .add (Text .of (archetype .getDependency ().getQualifiedName ()));
339359
340-
341360 // TODO: for now, this is stylistically similar to buildEnclosedElement,
342361 // but arguably a better way would be to move code for the check or cross into
343362 // the enclosing loop that builds the list.
344- return HtmlTree .SPAN (getResultGlyph (rPos ), Text .SPACE )
345- .add (HtmlTree .SPAN (contents ).setClass ("signature" ));
363+ ResultKind result = getResultKind (rPos );
364+
365+ return new ContentAndResultKind (HtmlTree .SPAN (result .getContent (), Text .SPACE )
366+ .add (HtmlTree .SPAN (contents ).setClass ("signature" )), result );
346367 }
347368}
0 commit comments