Bug 562310 - Get rid of pre-JDT 3.14 logic in ModuleSupport

Change-Id: I32481b2caa354e2d63c7fd600662e5c72b7bbfac
Signed-off-by: Mickael Istria <mistria@redhat.com>
diff --git a/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/InternalModuleSupport.java b/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/InternalModuleSupport.java
deleted file mode 100644
index a2505ff..0000000
--- a/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/InternalModuleSupport.java
+++ /dev/null
@@ -1,329 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2017 Red Hat, Inc.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License 2.0
- * which accompanies this distribution, and is available at
- * https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- *
- * Contributors:
- *      Red Hat, Inc. - initial API and implementation
- *      Metron, Inc. - support for provides/uses directives
- *******************************************************************************/
-
-package org.eclipse.m2e.jdt.internal;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.jar.JarFile;
-import java.util.jar.Manifest;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.eclipse.core.resources.IFolder;
-import org.eclipse.core.resources.IProject;
-import org.eclipse.core.resources.IWorkspaceRoot;
-import org.eclipse.core.resources.ResourcesPlugin;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IPath;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.NullProgressMonitor;
-import org.eclipse.core.runtime.Path;
-import org.eclipse.debug.core.ILaunchConfiguration;
-import org.eclipse.jdt.core.IClasspathAttribute;
-import org.eclipse.jdt.core.IClasspathEntry;
-import org.eclipse.jdt.core.IJavaProject;
-import org.eclipse.jdt.core.IModuleDescription;
-import org.eclipse.jdt.core.JavaCore;
-import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
-import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
-import org.eclipse.jdt.internal.compiler.env.AutomaticModuleNaming;
-import org.eclipse.jdt.internal.compiler.env.IModule;
-import org.eclipse.jdt.internal.launching.RuntimeClasspathEntry;
-import org.eclipse.jdt.launching.IRuntimeClasspathEntry;
-import org.eclipse.jdt.launching.JavaRuntime;
-
-import org.apache.maven.project.MavenProject;
-
-import org.eclipse.m2e.core.MavenPlugin;
-import org.eclipse.m2e.core.project.IMavenProjectFacade;
-import org.eclipse.m2e.jdt.IClasspathDescriptor;
-import org.eclipse.m2e.jdt.IClasspathEntryDescriptor;
-
-
-/**
- * Helper for Java Module Support relying on new JDT classes
- *
- * @author Fred Bricon
- * @since 1.8.2
- */
-@Deprecated
-@SuppressWarnings("restriction")
-class InternalModuleSupport {
-
-  private static final Logger log = LoggerFactory.getLogger(InternalModuleSupport.class);
-
-  /**
-   * This is a copy of the constant of org.eclipse.jdt.launching.IRuntimeClasspathEntry.PATCH_MODULE. Having this copy
-   * allows to compile and run with 4.7.1a
-   */
-  private static final int PATCH_MODULE = 6;
-
-  /**
-   * Sets <code>module</code flag to <code>true</code> to classpath dependencies declared in module-info.java
-   * 
-   * @param facade a Maven facade project
-   * @param classpath a classpath descriptor
-   * @param monitor a progress monitor
-   */
-  public static void configureClasspath(IMavenProjectFacade facade, IClasspathDescriptor classpath,
-      IProgressMonitor monitor) {
-    IJavaProject javaProject = JavaCore.create(facade.getProject());
-    if(javaProject == null || !javaProject.exists()) {
-      return;
-    }
-
-    int targetCompliance = 8;
-    String option = javaProject.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, true);
-    if(option != null) {
-      if(option.startsWith("1.")) {
-        option = option.substring("1.".length());
-      }
-      try {
-        targetCompliance = Integer.parseInt(option);
-      } catch(NumberFormatException ex) {
-        log.error(ex.getMessage(), ex);
-      }
-    }
-    if(targetCompliance < 9) {
-      return;
-    }
-
-    if(monitor == null) {
-      monitor = new NullProgressMonitor();
-    }
-
-    InternalModuleInfo moduleInfo = getModuleInfo(javaProject, monitor);
-    if(moduleInfo == null) {
-      return;
-    }
-
-    Map<String, InternalModuleInfo> entryModuleInfos = new LinkedHashMap<>();
-    Map<String, IClasspathEntryDescriptor> entryDescriptors = new LinkedHashMap<>();
-    for(IClasspathEntryDescriptor entryDescriptor : classpath.getEntryDescriptors()) {
-      if(monitor.isCanceled()) {
-        return;
-      }
-      InternalModuleInfo entryModuleInfo = getModuleInfo(entryDescriptor, monitor, targetCompliance);
-      if(entryModuleInfo != null) {
-        entryModuleInfos.put(entryModuleInfo.name, entryModuleInfo);//potentially suppresses duplicate entries from the same workspace project, with different classifiers
-        entryDescriptors.put(entryModuleInfo.name, entryDescriptor);
-      }
-    }
-
-    Set<String> neededModuleNames = collectModulesNeededTransitively(moduleInfo, entryModuleInfos);
-    if(monitor.isCanceled()) {
-      return;
-    }
-
-    entryDescriptors.forEach((entryModuleName, entry) ->
-    {
-      if(neededModuleNames.contains(entryModuleName)) {
-        entry.setClasspathAttribute(IClasspathAttribute.MODULE, Boolean.TRUE.toString());
-      }
-    });
-  }
-
-  private static Set<String> collectModulesNeededTransitively(InternalModuleInfo module,
-      Map<String, InternalModuleInfo> classpathModules) {
-    Set<String> result = new LinkedHashSet<>();
-    Function<InternalModuleInfo, Set<String>> neededModulesLookup = createNeededModulesLookup(classpathModules);
-    Set<String> todo = neededModulesLookup.apply(module);
-    while(!todo.isEmpty()) {
-      Set<String> todoNext = new LinkedHashSet<>();
-      for(String neededModuleName : todo) {
-        if(result.add(neededModuleName)) {
-          InternalModuleInfo neededModule = classpathModules.get(neededModuleName);
-          todoNext.addAll(neededModulesLookup.apply(neededModule));
-        } else {
-          //already checked that module
-        }
-      }
-      todo = todoNext;
-    }
-    return result;
-  }
-
-  /**
-   * Returns a function that takes a {@link ModuleInfo}, and looks up the names of the modules needed by the given
-   * module -- including modules it requires, and also modules that provide services it uses.
-   */
-  private static Function<InternalModuleInfo, Set<String>> createNeededModulesLookup(
-      Map<String, InternalModuleInfo> classpathModules) {
-    Map<String, Set<String>> providersByServiceName = new LinkedHashMap<>();
-    for(InternalModuleInfo classpathModule : classpathModules.values()) {
-      for(String serviceName : classpathModule.providedServiceNames) {
-        providersByServiceName.computeIfAbsent(serviceName, k -> new LinkedHashSet<>()).add(classpathModule.name);
-      }
-    }
-    return (module) ->
-    {
-      if(module != null) {
-        Set<String> result = new LinkedHashSet<>();
-        result.addAll(module.requiredModuleNames);
-        for(String serviceName : module.usedServiceNames) {
-          Set<String> providerNames = providersByServiceName.getOrDefault(serviceName, Collections.emptySet());
-          result.addAll(providerNames);
-        }
-        return result;
-      }
-      return Collections.emptySet();
-    };
-  }
-
-  private static InternalModuleInfo getModuleInfo(IClasspathEntryDescriptor entry, IProgressMonitor monitor,
-      int targetCompliance) {
-    if(entry != null && !monitor.isCanceled()) {
-      if(IClasspathEntry.CPE_LIBRARY == entry.getEntryKind()) {
-        return getModuleInfo(entry.getPath().toFile(), targetCompliance);
-      } else if(IClasspathEntry.CPE_PROJECT == entry.getEntryKind()) {
-        return getModuleInfo(getJavaProject(entry.getPath()), monitor);
-      }
-    }
-    return null;
-  }
-
-  static InternalModuleInfo getModuleInfo(IJavaProject project, IProgressMonitor monitor) {
-    if(project != null) {
-      try {
-        IModuleDescription moduleDescription = project.getModuleDescription();
-        if(moduleDescription != null) {
-          return InternalModuleInfo.fromDescription(moduleDescription);
-        }
-
-        String buildName = null;
-        IMavenProjectFacade facade = MavenPlugin.getMavenProjectRegistry().getProject(project.getProject());
-        if(facade != null) {
-          MavenProject mavenProject = facade.getMavenProject(monitor);
-          if(mavenProject != null) {
-            buildName = mavenProject.getBuild().getFinalName();
-          }
-        }
-        if(buildName == null || buildName.isEmpty()) {
-          buildName = project.getElementName();
-        }
-        String moduleName = new String(AutomaticModuleNaming.determineAutomaticModuleName(buildName, false, null));
-        return InternalModuleInfo.withAutomaticName(moduleName);
-
-      } catch(CoreException ex) {
-        log.error(ex.getMessage(), ex);
-      }
-    }
-    return null;
-  }
-
-  private static InternalModuleInfo getModuleInfo(File file, int targetCompliance) {
-    if(!file.isFile()) {
-      return null;
-    }
-    try (JarFile jar = new JarFile(file, false)) {
-      Manifest manifest = jar.getManifest();
-      boolean isMultiRelease = false;
-      if(manifest != null) {
-        isMultiRelease = "true".equalsIgnoreCase(manifest.getMainAttributes().getValue("Multi-Release"));
-      }
-      int compliance = isMultiRelease ? targetCompliance : 8;
-      for(int i = compliance; i >= 8; i-- ) {
-        String filename;
-        if(i == 8) {
-          // 8 represents unversioned module-info.class
-          filename = IModule.MODULE_INFO_CLASS;
-        } else {
-          filename = "META-INF/versions/" + i + "/" + IModule.MODULE_INFO_CLASS;
-        }
-        ClassFileReader reader = ClassFileReader.read(jar, filename);
-        if(reader != null) {
-          IModule module = reader.getModuleDeclaration();
-          if(module != null) {
-            return InternalModuleInfo.fromDeclaration(module);
-          }
-        }
-      }
-      if(manifest != null) {
-        // optimization: we already have the manifest, so directly check for Automatic-Module-Name
-        // rather than using AutomaticModuleNaming.determineAutomaticModuleName(String)
-        String automaticModuleName = manifest.getMainAttributes().getValue("Automatic-Module-Name");
-        if(automaticModuleName != null) {
-          return InternalModuleInfo.withAutomaticName(automaticModuleName);
-        }
-      }
-    } catch(ClassFormatException | IOException ex) {
-      log.error(ex.getMessage(), ex);
-    }
-    return InternalModuleInfo.withAutomaticNameFromFile(file);
-  }
-
-  private static IJavaProject getJavaProject(IPath projectPath) {
-    if(projectPath == null || projectPath.isEmpty()) {
-      return null;
-    }
-    IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
-    IProject project = root.getProject(projectPath.lastSegment());
-    if(project.isAccessible()) {
-      return JavaCore.create(project);
-    }
-    return null;
-  }
-
-  public static boolean isModuleEntry(IClasspathEntry entry) {
-    return Arrays.stream(entry.getExtraAttributes())
-        .anyMatch(p -> IClasspathAttribute.MODULE.equals(p.getName()) && "true".equals(p.getValue()));
-  }
-
-  public static int determineModularClasspathProperty(IClasspathEntry entry) {
-    return isModuleEntry(entry) ? IRuntimeClasspathEntry.MODULE_PATH : IRuntimeClasspathEntry.CLASS_PATH;
-  }
-
-  public static IRuntimeClasspathEntry createRuntimeClasspathEntry(IFolder folder, int classpathProperty,
-      IProject project) {
-    if(classpathProperty == IRuntimeClasspathEntry.MODULE_PATH && !folder.exists(new Path("module-info.class"))) {
-      classpathProperty = PATCH_MODULE;
-    }
-    IRuntimeClasspathEntry newArchiveRuntimeClasspathEntry = JavaRuntime
-        .newArchiveRuntimeClasspathEntry(folder.getFullPath(), classpathProperty);
-    if(classpathProperty == PATCH_MODULE) {
-      ((RuntimeClasspathEntry) newArchiveRuntimeClasspathEntry).setJavaProject(JavaCore.create(project));
-    }
-    return newArchiveRuntimeClasspathEntry;
-  }
-
-  public static int determineClasspathPropertyForMainProject(boolean isModularConfiguration, IJavaProject javaProject) {
-    if(!isModularConfiguration) {
-      return IRuntimeClasspathEntry.USER_CLASSES;
-    } else if(!JavaRuntime.isModularProject(javaProject)) {
-      return IRuntimeClasspathEntry.CLASS_PATH;
-    } else {
-      return IRuntimeClasspathEntry.MODULE_PATH;
-    }
-  }
-
-  public static boolean isModularConfiguration(ILaunchConfiguration configuration) {
-    return JavaRuntime.isModularConfiguration(configuration);
-  }
-
-  public static IRuntimeClasspathEntry newModularProjectRuntimeClasspathEntry(IJavaProject javaProject) {
-    return JavaRuntime.newProjectRuntimeClasspathEntry(javaProject,
-        JavaRuntime.isModularProject(javaProject) ? IRuntimeClasspathEntry.MODULE_PATH
-            : IRuntimeClasspathEntry.CLASS_PATH);
-  }
-
-}
diff --git a/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/ModuleSupport.java b/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/ModuleSupport.java
index dd956ad..d804d75 100644
--- a/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/ModuleSupport.java
+++ b/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/ModuleSupport.java
@@ -14,24 +14,50 @@
 
 package org.eclipse.m2e.jdt.internal;
 
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.eclipse.core.resources.IFolder;
 import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jdt.core.IClasspathAttribute;
 import org.eclipse.jdt.core.IClasspathEntry;
 import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IModuleDescription;
 import org.eclipse.jdt.core.JavaCore;
-import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
+import org.eclipse.jdt.internal.compiler.env.AutomaticModuleNaming;
+import org.eclipse.jdt.internal.compiler.env.IModule;
+import org.eclipse.jdt.internal.launching.RuntimeClasspathEntry;
 import org.eclipse.jdt.launching.IRuntimeClasspathEntry;
 import org.eclipse.jdt.launching.JavaRuntime;
 
+import org.apache.maven.project.MavenProject;
+
+import org.eclipse.m2e.core.MavenPlugin;
 import org.eclipse.m2e.core.internal.IMavenConstants;
 import org.eclipse.m2e.core.project.IMavenProjectFacade;
 import org.eclipse.m2e.jdt.IClasspathDescriptor;
+import org.eclipse.m2e.jdt.IClasspathEntryDescriptor;
 
 
 /**
@@ -40,34 +66,13 @@
  * @author Fred Bricon
  * @since 1.8.2
  */
+@SuppressWarnings("restriction")
 public class ModuleSupport {
 
   public static final String MODULE_INFO_JAVA = "module-info.java";
 
-  static final boolean IS_MODULE_SUPPORT_AVAILABLE;
-
-  static final boolean IS_PATCH_MODULE_SUPPORT_AVAILABLE;
-
   private static final Logger log = LoggerFactory.getLogger(ModuleSupport.class);
 
-  static {
-    boolean isModuleSupportAvailable = false;
-    boolean isPatchModuleSupportAvailable = false;
-    try {
-      Class.forName("org.eclipse.jdt.core.IModuleDescription");
-      isModuleSupportAvailable = true;
-      try {
-        IRuntimeClasspathEntry.class.getDeclaredField("PATCH_MODULE");
-        isPatchModuleSupportAvailable = true;
-      } catch(NoSuchFieldException | SecurityException ignored) {
-      }
-    } catch(ClassNotFoundException ignored) {
-    }
-
-    IS_MODULE_SUPPORT_AVAILABLE = isModuleSupportAvailable;
-    IS_PATCH_MODULE_SUPPORT_AVAILABLE = isPatchModuleSupportAvailable;
-  }
-
   /**
    * Sets <code>module</code> flag to <code>true</code> to classpath dependencies declared in module-info.java
    * 
@@ -76,47 +81,238 @@
    * @param monitor a progress monitor
    */
   public static void configureClasspath(IMavenProjectFacade facade, IClasspathDescriptor classpath,
-      IProgressMonitor monitor) throws CoreException {
-    if(!IS_MODULE_SUPPORT_AVAILABLE || classpath == null) {
+      IProgressMonitor monitor) {
+    IJavaProject javaProject = JavaCore.create(facade.getProject());
+    if(javaProject == null || !javaProject.exists() || classpath == null) {
       return;
     }
-    InternalModuleSupport.configureClasspath(facade, classpath, monitor);
+
+    int targetCompliance = 8;
+    String option = javaProject.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, true);
+    if(option != null) {
+      if(option.startsWith("1.")) {
+        option = option.substring("1.".length());
+      }
+      try {
+        targetCompliance = Integer.parseInt(option);
+      } catch(NumberFormatException ex) {
+        log.error(ex.getMessage(), ex);
+      }
+    }
+    if(targetCompliance < 9) {
+      return;
+    }
+
+    if(monitor == null) {
+      monitor = new NullProgressMonitor();
+    }
+
+    InternalModuleInfo moduleInfo = getModuleInfo(javaProject, monitor);
+    if(moduleInfo == null) {
+      return;
+    }
+
+    Map<String, InternalModuleInfo> entryModuleInfos = new LinkedHashMap<>();
+    Map<String, IClasspathEntryDescriptor> entryDescriptors = new LinkedHashMap<>();
+    for(IClasspathEntryDescriptor entryDescriptor : classpath.getEntryDescriptors()) {
+      if(monitor.isCanceled()) {
+        return;
+      }
+      InternalModuleInfo entryModuleInfo = getModuleInfo(entryDescriptor, monitor, targetCompliance);
+      if(entryModuleInfo != null) {
+        entryModuleInfos.put(entryModuleInfo.name, entryModuleInfo);//potentially suppresses duplicate entries from the same workspace project, with different classifiers
+        entryDescriptors.put(entryModuleInfo.name, entryDescriptor);
+      }
+    }
+
+    Set<String> neededModuleNames = collectModulesNeededTransitively(moduleInfo, entryModuleInfos);
+    if(monitor.isCanceled()) {
+      return;
+    }
+
+    entryDescriptors.forEach((entryModuleName, entry) -> {
+      if(neededModuleNames.contains(entryModuleName)) {
+        entry.setClasspathAttribute(IClasspathAttribute.MODULE, Boolean.TRUE.toString());
+      }
+    });
+  }
+
+  private static Set<String> collectModulesNeededTransitively(InternalModuleInfo module,
+      Map<String, InternalModuleInfo> classpathModules) {
+    Set<String> result = new LinkedHashSet<>();
+    Function<InternalModuleInfo, Set<String>> neededModulesLookup = createNeededModulesLookup(classpathModules);
+    Set<String> todo = neededModulesLookup.apply(module);
+    while(!todo.isEmpty()) {
+      Set<String> todoNext = new LinkedHashSet<>();
+      for(String neededModuleName : todo) {
+        if(result.add(neededModuleName)) {
+          InternalModuleInfo neededModule = classpathModules.get(neededModuleName);
+          todoNext.addAll(neededModulesLookup.apply(neededModule));
+        } else {
+          //already checked that module
+        }
+      }
+      todo = todoNext;
+    }
+    return result;
+  }
+
+  /**
+   * Returns a function that takes a {@link ModuleInfo}, and looks up the names of the modules needed by the given
+   * module -- including modules it requires, and also modules that provide services it uses.
+   */
+  private static Function<InternalModuleInfo, Set<String>> createNeededModulesLookup(
+      Map<String, InternalModuleInfo> classpathModules) {
+    Map<String, Set<String>> providersByServiceName = new LinkedHashMap<>();
+    for(InternalModuleInfo classpathModule : classpathModules.values()) {
+      for(String serviceName : classpathModule.providedServiceNames) {
+        providersByServiceName.computeIfAbsent(serviceName, k -> new LinkedHashSet<>()).add(classpathModule.name);
+      }
+    }
+    return (module) -> {
+      if(module != null) {
+        Set<String> result = new LinkedHashSet<>();
+        result.addAll(module.requiredModuleNames);
+        for(String serviceName : module.usedServiceNames) {
+          Set<String> providerNames = providersByServiceName.getOrDefault(serviceName, Collections.emptySet());
+          result.addAll(providerNames);
+        }
+        return result;
+      }
+      return Collections.emptySet();
+    };
+  }
+
+  private static InternalModuleInfo getModuleInfo(IClasspathEntryDescriptor entry, IProgressMonitor monitor,
+      int targetCompliance) {
+    if(entry != null && !monitor.isCanceled()) {
+      if(IClasspathEntry.CPE_LIBRARY == entry.getEntryKind()) {
+        return getModuleInfo(entry.getPath().toFile(), targetCompliance);
+      } else if(IClasspathEntry.CPE_PROJECT == entry.getEntryKind()) {
+        return getModuleInfo(getJavaProject(entry.getPath()), monitor);
+      }
+    }
+    return null;
+  }
+
+  static InternalModuleInfo getModuleInfo(IJavaProject project, IProgressMonitor monitor) {
+    if(project != null) {
+      try {
+        IModuleDescription moduleDescription = project.getModuleDescription();
+        if(moduleDescription != null) {
+          return InternalModuleInfo.fromDescription(moduleDescription);
+        }
+
+        String buildName = null;
+        IMavenProjectFacade facade = MavenPlugin.getMavenProjectRegistry().getProject(project.getProject());
+        if(facade != null) {
+          MavenProject mavenProject = facade.getMavenProject(monitor);
+          if(mavenProject != null) {
+            buildName = mavenProject.getBuild().getFinalName();
+          }
+        }
+        if(buildName == null || buildName.isEmpty()) {
+          buildName = project.getElementName();
+        }
+        String moduleName = new String(AutomaticModuleNaming.determineAutomaticModuleName(buildName, false, null));
+        return InternalModuleInfo.withAutomaticName(moduleName);
+
+      } catch(CoreException ex) {
+        log.error(ex.getMessage(), ex);
+      }
+    }
+    return null;
+  }
+
+  private static InternalModuleInfo getModuleInfo(File file, int targetCompliance) {
+    if(!file.isFile()) {
+      return null;
+    }
+    try (JarFile jar = new JarFile(file, false)) {
+      Manifest manifest = jar.getManifest();
+      boolean isMultiRelease = false;
+      if(manifest != null) {
+        isMultiRelease = "true".equalsIgnoreCase(manifest.getMainAttributes().getValue("Multi-Release"));
+      }
+      int compliance = isMultiRelease ? targetCompliance : 8;
+      for(int i = compliance; i >= 8; i-- ) {
+        String filename;
+        if(i == 8) {
+          // 8 represents unversioned module-info.class
+          filename = IModule.MODULE_INFO_CLASS;
+        } else {
+          filename = "META-INF/versions/" + i + "/" + IModule.MODULE_INFO_CLASS;
+        }
+        ClassFileReader reader = ClassFileReader.read(jar, filename);
+        if(reader != null) {
+          IModule module = reader.getModuleDeclaration();
+          if(module != null) {
+            return InternalModuleInfo.fromDeclaration(module);
+          }
+        }
+      }
+      if(manifest != null) {
+        // optimization: we already have the manifest, so directly check for Automatic-Module-Name
+        // rather than using AutomaticModuleNaming.determineAutomaticModuleName(String)
+        String automaticModuleName = manifest.getMainAttributes().getValue("Automatic-Module-Name");
+        if(automaticModuleName != null) {
+          return InternalModuleInfo.withAutomaticName(automaticModuleName);
+        }
+      }
+    } catch(ClassFormatException | IOException ex) {
+      log.error(ex.getMessage(), ex);
+    }
+    return InternalModuleInfo.withAutomaticNameFromFile(file);
+  }
+
+  private static IJavaProject getJavaProject(IPath projectPath) {
+    if(projectPath == null || projectPath.isEmpty()) {
+      return null;
+    }
+    IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
+    IProject project = root.getProject(projectPath.lastSegment());
+    if(project.isAccessible()) {
+      return JavaCore.create(project);
+    }
+    return null;
+  }
+
+  public static boolean isModuleEntry(IClasspathEntry entry) {
+    return Arrays.stream(entry.getExtraAttributes())
+        .anyMatch(p -> IClasspathAttribute.MODULE.equals(p.getName()) && "true".equals(p.getValue()));
   }
 
   public static int determineModularClasspathProperty(IClasspathEntry entry) {
-    if(!IS_PATCH_MODULE_SUPPORT_AVAILABLE) {
-      return IRuntimeClasspathEntry.USER_CLASSES;
-    }
-    return InternalModuleSupport.determineModularClasspathProperty(entry);
+    return isModuleEntry(entry) ? IRuntimeClasspathEntry.MODULE_PATH : IRuntimeClasspathEntry.CLASS_PATH;
   }
 
   public static IRuntimeClasspathEntry createRuntimeClasspathEntry(IFolder folder, int classpathProperty,
       IProject project) {
-    if(!IS_PATCH_MODULE_SUPPORT_AVAILABLE) {
-      return JavaRuntime.newArchiveRuntimeClasspathEntry(folder.getFullPath());
+    if(classpathProperty == IRuntimeClasspathEntry.MODULE_PATH && !folder.exists(new Path("module-info.class"))) {
+      classpathProperty = IRuntimeClasspathEntry.PATCH_MODULE;
     }
-    return InternalModuleSupport.createRuntimeClasspathEntry(folder, classpathProperty, project);
+    IRuntimeClasspathEntry newArchiveRuntimeClasspathEntry = JavaRuntime
+        .newArchiveRuntimeClasspathEntry(folder.getFullPath(), classpathProperty);
+    if(classpathProperty == IRuntimeClasspathEntry.PATCH_MODULE) {
+      ((RuntimeClasspathEntry) newArchiveRuntimeClasspathEntry).setJavaProject(JavaCore.create(project));
+    }
+    return newArchiveRuntimeClasspathEntry;
   }
 
   public static int determineClasspathPropertyForMainProject(boolean isModularConfiguration, IJavaProject javaProject) {
-    if(!IS_PATCH_MODULE_SUPPORT_AVAILABLE) {
+    if(!isModularConfiguration) {
       return IRuntimeClasspathEntry.USER_CLASSES;
+    } else if(!JavaRuntime.isModularProject(javaProject)) {
+      return IRuntimeClasspathEntry.CLASS_PATH;
+    } else {
+      return IRuntimeClasspathEntry.MODULE_PATH;
     }
-    return InternalModuleSupport.determineClasspathPropertyForMainProject(isModularConfiguration, javaProject);
-  }
-
-  public static boolean isModularConfiguration(ILaunchConfiguration configuration) {
-    if(!IS_PATCH_MODULE_SUPPORT_AVAILABLE) {
-      return false;
-    }
-    return InternalModuleSupport.isModularConfiguration(configuration);
   }
 
   public static IRuntimeClasspathEntry newModularProjectRuntimeClasspathEntry(IJavaProject javaProject) {
-    if(!IS_PATCH_MODULE_SUPPORT_AVAILABLE) {
-      return JavaRuntime.newProjectRuntimeClasspathEntry(javaProject);
-    }
-    return InternalModuleSupport.newModularProjectRuntimeClasspathEntry(javaProject);
+    return JavaRuntime.newProjectRuntimeClasspathEntry(javaProject,
+        JavaRuntime.isModularProject(javaProject) ? IRuntimeClasspathEntry.MODULE_PATH
+            : IRuntimeClasspathEntry.CLASS_PATH);
   }
 
   public static boolean isMavenJavaProject(IProject project) {
@@ -128,8 +324,4 @@
     }
     return false;
   }
-
-  static InternalModuleInfo getModuleInfo(IJavaProject jp, IProgressMonitor monitor) throws JavaModelException {
-    return InternalModuleSupport.getModuleInfo(jp, monitor);
-  }
 }
diff --git a/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/launch/MavenRuntimeClasspathProvider.java b/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/launch/MavenRuntimeClasspathProvider.java
index 7d719a3..c58d828 100644
--- a/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/launch/MavenRuntimeClasspathProvider.java
+++ b/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/launch/MavenRuntimeClasspathProvider.java
@@ -110,7 +110,7 @@
 
   public IRuntimeClasspathEntry[] computeUnresolvedClasspath(final ILaunchConfiguration configuration)
       throws CoreException {
-    boolean isModular = ModuleSupport.isModularConfiguration(configuration);
+    boolean isModular = JavaRuntime.isModularConfiguration(configuration);
     boolean useDefault = configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, true);
     if(useDefault) {
       IJavaProject javaProject = JavaRuntime.getJavaProject(configuration);