3232import java .util .Arrays ;
3333import java .util .Collection ;
3434import java .util .Collections ;
35+ import java .util .Comparator ;
3536import java .util .HashMap ;
3637import java .util .HashSet ;
3738import java .util .Hashtable ;
@@ -1230,6 +1231,10 @@ public static IRuntimeClasspathProvider getSourceLookupPathProvider(ILaunchConfi
12301231 * @since 2.0
12311232 */
12321233 public static IRuntimeClasspathEntry [] resolveRuntimeClasspathEntry (IRuntimeClasspathEntry entry , ILaunchConfiguration configuration ) throws CoreException {
1234+ return resolveRuntimeClasspathEntry (entry , configuration , JavaProject .NO_RELEASE );
1235+ }
1236+
1237+ static IRuntimeClasspathEntry [] resolveRuntimeClasspathEntry (IRuntimeClasspathEntry entry , ILaunchConfiguration configuration , int runtimeJavaVersion ) throws CoreException {
12331238 boolean excludeTestCode = configuration .getAttribute (IJavaLaunchConfigurationConstants .ATTR_EXCLUDE_TEST_CODE , false );
12341239 switch (entry .getType ()) {
12351240 case IRuntimeClasspathEntry .PROJECT :
@@ -1244,7 +1249,7 @@ public static IRuntimeClasspathEntry[] resolveRuntimeClasspathEntry(IRuntimeClas
12441249 IClasspathAttribute [] attributes = entry .getClasspathEntry ().getExtraAttributes ();
12451250 boolean withoutTestCode = entry .getClasspathEntry ().isWithoutTestCode ();
12461251 IRuntimeClasspathEntry [] entries = resolveOutputLocations (project , entry .getClasspathProperty (), attributes , excludeTestCode
1247- || withoutTestCode );
1252+ || withoutTestCode , runtimeJavaVersion );
12481253 if (entries != null ) {
12491254 return entries ;
12501255 }
@@ -1373,20 +1378,35 @@ private static IRuntimeClasspathEntry[] resolveVariableEntry(IRuntimeClasspathEn
13731378 * extra attributes of the original classpath entry
13741379 * @param excludeTestCode
13751380 * if true, output folders corresponding to test sources are excluded
1381+ * @param runtimeJavaVersion
1382+ * the java runtime version used
13761383 *
13771384 * @return IRuntimeClasspathEntry[] or <code>null</code>
13781385 * @throws CoreException
13791386 * if output resolution encounters a problem
13801387 */
1381- private static IRuntimeClasspathEntry [] resolveOutputLocations (IJavaProject project , int classpathProperty , IClasspathAttribute [] attributes , boolean excludeTestCode ) throws CoreException {
1388+ private static IRuntimeClasspathEntry [] resolveOutputLocations (IJavaProject project , int classpathProperty , IClasspathAttribute [] attributes , boolean excludeTestCode , int runtimeJavaVersion ) throws CoreException {
13821389 List <IPath > nonDefault = new ArrayList <>();
1390+ List <PathWithRelease > multiRelease = new ArrayList <>();
13831391 boolean defaultUsedByNonTest = false ;
1392+ IPath def = project .getOutputLocation ();
13841393 if (project .exists () && project .getProject ().isOpen ()) {
13851394 IClasspathEntry entries [] = project .getRawClasspath ();
13861395 for (int i = 0 ; i < entries .length ; i ++) {
13871396 IClasspathEntry classpathEntry = entries [i ];
13881397 if (classpathEntry .getEntryKind () == IClasspathEntry .CPE_SOURCE ) {
1398+ int release = getRelease (classpathEntry );
1399+ if (release > runtimeJavaVersion ) {
1400+ // ignore entries that target a higher release
1401+ continue ;
1402+ }
13891403 IPath path = classpathEntry .getOutputLocation ();
1404+ if (release >= JavaProject .FIRST_MULTI_RELEASE ) {
1405+ // needs special treatment!
1406+ IPath mrOutput = Objects .requireNonNullElse (path , def ).append (new Path (String .format ("META-INF/versions/%s" , release ))); //$NON-NLS-1$
1407+ multiRelease .add (new PathWithRelease (mrOutput , release ));
1408+ continue ;
1409+ }
13901410 if (path != null ) {
13911411 if (!(excludeTestCode && classpathEntry .isTest ())) {
13921412 nonDefault .add (path );
@@ -1400,17 +1420,24 @@ private static IRuntimeClasspathEntry[] resolveOutputLocations(IJavaProject proj
14001420 }
14011421 }
14021422 boolean isModular = project .getOwnModuleDescription () != null ;
1403- if (nonDefault .isEmpty () && !isModular && !excludeTestCode ) {
1423+ if (nonDefault .isEmpty () && multiRelease . isEmpty () && !isModular && !excludeTestCode ) {
14041424 // return here only if non-modular, because patch-module might be needed otherwise
14051425 return null ;
14061426 }
14071427 // add the default location if not already included
1408- IPath def = project .getOutputLocation ();
14091428 if (!excludeTestCode || defaultUsedByNonTest ) {
14101429 if (!nonDefault .contains (def )) {
14111430 nonDefault .add (def );
14121431 }
14131432 }
1433+ if (!multiRelease .isEmpty ()) {
1434+ // now sort and add the multi-release output locations, must be with highest release first so that such types are found before lower ones
1435+ multiRelease .sort (Comparator .comparingInt (PathWithRelease ::release ));
1436+ for (PathWithRelease pathWithRelease : multiRelease ) {
1437+ nonDefault .add (0 , pathWithRelease .path ());
1438+ }
1439+ }
1440+
14141441 IRuntimeClasspathEntry [] locations = new IRuntimeClasspathEntry [nonDefault .size ()];
14151442 for (int i = 0 ; i < locations .length ; i ++) {
14161443 IClasspathEntry newEntry = JavaCore .newLibraryEntry (nonDefault .get (i ), null , null , null , attributes , false );
@@ -1425,6 +1452,18 @@ private static IRuntimeClasspathEntry[] resolveOutputLocations(IJavaProject proj
14251452 return locations ;
14261453 }
14271454
1455+ private static int getRelease (IClasspathEntry classpathEntry ) {
1456+ String releaseAttribute = ClasspathEntry .getExtraAttribute (classpathEntry , IClasspathAttribute .RELEASE );
1457+ if (releaseAttribute != null ) {
1458+ try {
1459+ return Integer .parseInt (releaseAttribute );
1460+ } catch (RuntimeException e ) {
1461+ // can't use it then!
1462+ }
1463+ }
1464+ return JavaProject .NO_RELEASE ;
1465+ }
1466+
14281467 private static boolean containsModuleInfo (IRuntimeClasspathEntry entry ) {
14291468 return new File (entry .getLocation () + File .separator + "module-info.class" ).exists (); //$NON-NLS-1$
14301469 }
@@ -1491,7 +1530,7 @@ public static IRuntimeClasspathEntry[] resolveRuntimeClasspathEntry(IRuntimeClas
14911530 IClasspathAttribute [] attributes = entry .getClasspathEntry ().getExtraAttributes ();
14921531 boolean withoutTestCode = entry .getClasspathEntry ().isWithoutTestCode ();
14931532 IRuntimeClasspathEntry [] entries = resolveOutputLocations (jp , entry .getClasspathProperty (), attributes , excludeTestCode
1494- || withoutTestCode );
1533+ || withoutTestCode , JavaProject . NO_RELEASE );
14951534 if (entries != null ) {
14961535 return entries ;
14971536 }
@@ -1671,10 +1710,11 @@ public static IRuntimeClasspathEntry[] computeUnresolvedRuntimeClasspath(ILaunch
16711710 * @since 2.0
16721711 */
16731712 public static IRuntimeClasspathEntry [] resolveRuntimeClasspath (IRuntimeClasspathEntry [] entries , ILaunchConfiguration configuration ) throws CoreException {
1713+ IRuntimeClasspathProvider classpathProvider = getClasspathProvider (configuration );
16741714 if (!isModularConfiguration (configuration )) {
1675- return getClasspathProvider ( configuration ) .resolveClasspath (entries , configuration );
1715+ return classpathProvider .resolveClasspath (entries , configuration );
16761716 }
1677- IRuntimeClasspathEntry [] entries1 = getClasspathProvider ( configuration ) .resolveClasspath (entries , configuration );
1717+ IRuntimeClasspathEntry [] entries1 = classpathProvider .resolveClasspath (entries , configuration );
16781718 List <IRuntimeClasspathEntry > entries2 = new ArrayList <>(entries1 .length );
16791719 IJavaProject project ;
16801720 try {
@@ -3759,4 +3799,8 @@ private static String joinedSortedList(Collection<String> list) {
37593799 Arrays .sort (limitArray );
37603800 return String .join (COMMA , limitArray );
37613801 }
3802+
3803+ private static record PathWithRelease (IPath path , int release ) {
3804+
3805+ }
37623806}
0 commit comments