Bug 546797 - [9][build path] Polish the new UI for modularity details

- textual rendering of patch-module (with absolute paths)
- explanation label in the Show JPMS Options dialog
- dis/enablement for the new button
- text polish
- robustness wrt closed projects
- offer to add system modules needed for add-reads
- offer tab switching from add-reads and remove(module)
- split module search (for correctness & speed):
  - system modules only via the projects JRE entry
  - only non-system modules via workspace search
- when current project is a patch, show the patched module as focus mod
  - required: improved CPListElement.{hashCode,equals}
- polish null return vs. exception
- create module attribute on source folder of focus project if needed

Change-Id: I379ecdee3b3116820af5fc15fae6873542723852
Signed-off-by: Stephan Herrmann <stephan.herrmann@berlin.de>
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/NewWizardMessages.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/NewWizardMessages.java
index d066b1f..20c3ff7 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/NewWizardMessages.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/NewWizardMessages.java
@@ -517,6 +517,7 @@
 	public static String ModuleDependenciesPage_modules_label;
 	public static String ModuleDependenciesPage_addSystemModule_button;
 	public static String ModuleDependenciesPage_details_label;
+	public static String ModuleDependenciesPage_nonModularProject_dummy;
 	// buttons:
 	public static String ModuleDependenciesPage_modules_remove_button;
 	public static String ModuleDependenciesPage_modules_read_button;
@@ -525,19 +526,28 @@
 	public static String ModuleDependenciesPage_modules_edit_button;
 	public static String ModuleDependenciesPage_showJPMSOptions_button;
 	// dialogs:
-	public static String ModuleSelectionDialog_add_button;
-	public static String ModuleSelectionDialog_addSystemModules_message;
 	public static String ModuleSelectionDialog_addSystemModules_title;
+	public static String ModuleSelectionDialog_addSystemModules_message;
+	public static String ModuleSelectionDialog_selectAll_button;
+	public static String ModuleSelectionDialog_add_button;
+
 	public static String ModuleSelectionDialog_selectModule_title;
 	public static String ModuleSelectionDialog_selectReadModule_message;
 	public static String ModuleSelectionDialog_searchModules_job;
 	public static String ModuleSelectionDialog_searchModules_temp_message;
-	public static String ModuleSelectionDialog_selectAll_button;
+
+	public static String ModuleDependenciesAdapter_addSystemModule_title;
+	public static String ModuleDependenciesAdapter_addSystemModules_question;
+	public static String ModuleDependenciesAdapter_add_button;
+	public static String ModuleDependenciesAdapter_addReadsNotOnModulepath_error;
+	public static String ModuleDependenciesAdapter_goToLibrariesTab_button;
+	public static String ModuleDependenciesAdapter_goToProjectsTab_button;
 
 	public static String ModulePatchSourceSelectionDialog_patchSourceLocation_message;
 	public static String ModulePatchSourceSelectionDialog_patchSourceLocation_title;
 
 	public static String ShowJPMSOptionsDialog_dialog_title;
+	public static String ShowJPMSOptionsDialog_explanation_label;
 	public static String ShowJPMSOptionsDialog_close_button;
 	public static String ShowJPMSOptionsDialog_copyAndCopy_button;
 	public static String ShowJPMSOptionsDialog_empty_message;
@@ -547,17 +557,17 @@
 	public static String ModuleDependenciesPage_removingModule_message;
 	public static String ModuleDependenciesPage_removingModuleTransitive_message;
 	public static String ModuleDependenciesPage_remove_button;
-	public static String ModuleDependenciesPage_cancel_button;
-	public static String ModuleDependenciesPage_nonModularProject_dummy;
 	public static String ModuleDependenciesPage_removeCurrentModule_error;
 	public static String ModuleDependenciesPage_removeModule_error_with_hint;
 	public static String ModuleDependenciesPage_removeSystemModule_error_hint;
+
 	public static String ModuleDependenciesAdapter_patchConflict_title;
 	public static String ModuleDependenciesAdapter_patchConflict_message;
 	public static String ModuleDependenciesAdapter_patchOutputConflict_validationError;
 	public static String ModuleDependenciesAdapter_project_kind;
 	public static String ModuleDependenciesAdapter_sourceFolder_kind;
 	public static String ModuleDependenciesAdapter_configure_error;
+
 	// detail tree:
 	public static String ModuleDependenciesAdapter_declared_node;
 	public static String ModuleDependenciesAdapter_configured_node;
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/NewWizardMessages.properties b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/NewWizardMessages.properties
index b7a480e..0d508cf 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/NewWizardMessages.properties
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/NewWizardMessages.properties
@@ -552,30 +552,40 @@
 AddReadsBlock_targetModule_label=Target module
 
 ModuleDependenciesPage_modules_label=All Modules
-ModuleDependenciesPage_addSystemModule_button=Add System Module...
+ModuleDependenciesPage_addSystemModule_button=Add S&ystem Module...
 ModuleDependenciesPage_details_label=Details
+ModuleDependenciesPage_nonModularProject_dummy=Non-modular Project
 # buttons
 ModuleDependenciesPage_modules_remove_button=&Remove
 ModuleDependenciesPage_modules_read_button=Rea&d Module...
 ModuleDependenciesPage_modules_expose_package_button=E&xpose Package...
-ModuleDependenciesPage_modules_patch_button=&Patch with...
+ModuleDependenciesPage_modules_patch_button=Patch &with...
 ModuleDependenciesPage_modules_edit_button=&Edit...
 ModuleDependenciesPage_showJPMSOptions_button=Show &JPMS Options...
 # dialogs:
-ModuleSelectionDialog_add_button=&Add
+ModuleSelectionDialog_addSystemModules_title=Select system modules to add
 ModuleSelectionDialog_addSystemModules_message=Select modules to be added to the module path.\n\
 Transitively required modules will be automatically selected.
-ModuleSelectionDialog_addSystemModules_title=Select system modules to add
+ModuleSelectionDialog_selectAll_button=&Select all
+ModuleSelectionDialog_add_button=&Add
+
 ModuleSelectionDialog_selectModule_title=Select module
 ModuleSelectionDialog_selectReadModule_message=Select module to read
 ModuleSelectionDialog_searchModules_job=Searching modules in workspace
 ModuleSelectionDialog_searchModules_temp_message=Searching modules in workspace...
-ModuleSelectionDialog_selectAll_button=Select all
+
+ModuleDependenciesAdapter_addSystemModule_title=Add System Modules
+ModuleDependenciesAdapter_addSystemModules_question=The following system modules are not included, add them now?\n
+ModuleDependenciesAdapter_add_button=&Add
+ModuleDependenciesAdapter_addReadsNotOnModulepath_error=The module ''{0}'' must be added to the Modulepath before a reads edge can be created to it
+ModuleDependenciesAdapter_goToLibrariesTab_button=&Go to Libraries tab
+ModuleDependenciesAdapter_goToProjectsTab_button=&Go to Projects tab
 
 ModulePatchSourceSelectionDialog_patchSourceLocation_message=Select source locations (projects or source folders) that should be associated with the selected module ''{0}''
 ModulePatchSourceSelectionDialog_patchSourceLocation_title=Select source locations for patching
 
 ShowJPMSOptionsDialog_dialog_title=JPMS Options
+ShowJPMSOptionsDialog_explanation_label=Translation of all module-related options into the command line syntax as specified by the Java Platform Module System (JPMS):
 ShowJPMSOptionsDialog_close_button=&Close
 ShowJPMSOptionsDialog_copyAndCopy_button=C&opy && Close
 ShowJPMSOptionsDialog_empty_message=<no JPMS options configured>
@@ -584,16 +594,15 @@
 ModuleDependenciesPage_removeModule_dialog_title=Remove Module
 ModuleDependenciesPage_removingModule_message=Removing module ''{0}''
 ModuleDependenciesPage_removingModuleTransitive_message=Removing module ''{0}'' and modules depending on it:\n
-ModuleDependenciesPage_remove_button=Remove
-ModuleDependenciesPage_cancel_button=Cancel
-ModuleDependenciesPage_nonModularProject_dummy=Non-modular Project
+ModuleDependenciesPage_remove_button=&Remove
 ModuleDependenciesPage_removeCurrentModule_error=Cannot remove the current module
 ModuleDependenciesPage_removeModule_error_with_hint=Cannot remove module ''{0}''{1}
 ModuleDependenciesPage_removeSystemModule_error_hint=\nOnly system modules can be removed here.\n\
-To remove other modules please remove them from the ModulePath (tab Projects or Libraries).
+To remove other modules please remove them from the Modulepath (Projects or Libraries tab).
+
+ModuleDependenciesAdapter_patchConflict_title=Patch module conflict
 ModuleDependenciesAdapter_patchConflict_message=The {0} {1} was declared to patch module ''{2}''.\n\
 Do you want to remove this and associate {3} with module ''{4}'' instead?
-ModuleDependenciesAdapter_patchConflict_title=Patch module conflict
 ModuleDependenciesAdapter_patchOutputConflict_validationError=The following source locations cannot patch different modules because they share the same output location: {0}
 ModuleDependenciesAdapter_project_kind=project
 ModuleDependenciesAdapter_sourceFolder_kind=source folder
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/BuildPathBasePage.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/BuildPathBasePage.java
index 76f06ba..95ee69d 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/BuildPathBasePage.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/BuildPathBasePage.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2018 IBM Corporation and others.
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -344,7 +344,17 @@
 		}
 	}
 
-
+	protected void selectRootNode(TreeListDialogField<CPListElement> list, boolean modulePath) {
+		for (CPListElement cpListElement : list.getElements()) {
+			if (cpListElement instanceof RootCPListElement) {
+				RootCPListElement root= (RootCPListElement) cpListElement;
+				if (root.isModulePathRootNode() == modulePath) {
+					list.selectElements(new StructuredSelection(root));
+					return;
+				}
+			}
+		}
+	}
 
 	protected abstract class CPListAdapter implements IDialogFieldListener, ITreeListAdapter<CPListElement> {
 		private final Object[] EMPTY_ARR= new Object[0];
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/BuildPathsBlock.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/BuildPathsBlock.java
index 5aa1dc4..8bb52ab 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/BuildPathsBlock.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/BuildPathsBlock.java
@@ -272,7 +272,7 @@
 		item.setData(ordpage);
 		item.setControl(ordpage.getControl(folder));
 
-		fModulesPage= new ModuleDependenciesPage(fContext, fClassPathList, fPageContainer);
+		fModulesPage= new ModuleDependenciesPage(fContext, fClassPathList);
 		item= new TabItem(folder, SWT.NONE);
 		item.setText(NewWizardMessages.BuildPathsBlock_tab_modules);
 		item.setImage(JavaPluginImages.get(JavaPluginImages.IMG_OBJS_MODULE));
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/CPListElement.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/CPListElement.java
index cab371b..a3c475e 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/CPListElement.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/CPListElement.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2017 IBM Corporation and others.
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -599,7 +599,15 @@
 	public boolean equals(Object other) {
 		if (other != null && other.getClass().equals(getClass())) {
 			CPListElement elem= (CPListElement) other;
-			return getClasspathEntry().equals(elem.getClasspathEntry());
+			if (!getClasspathEntry().equals(elem.getClasspathEntry())) {
+				return false;
+			}
+			if (this.fModule != null && elem.fModule != null) {
+				if (!this.fModule.equals(elem.fModule)) {
+					return false;
+				}
+			}
+			return this.fModule == null && elem.fModule == null;
 		}
 		return false;
 	}
@@ -611,7 +619,11 @@
 	public int hashCode() {
 		if(fPath==null)
 			return super.hashCode();
-		return fPath.hashCode() + fEntryKind;
+		int code= fPath.hashCode() + fEntryKind;
+		if (this.fModule != null) {
+			code= 31 * code + this.fModule.hashCode();
+		}
+		return code;
 	}
 
 	@Override
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/LibrariesWorkbookPage.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/LibrariesWorkbookPage.java
index 97788d5..4f51da3 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/LibrariesWorkbookPage.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/LibrariesWorkbookPage.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2018 IBM Corporation and others.
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -1439,4 +1439,7 @@
     	fLibrariesList.setFocus();
     }
 
+	public void selectRootNode(boolean modulePath) {
+		selectRootNode(fLibrariesList, modulePath);
+	}
 }
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModuleDependenciesAdapter.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModuleDependenciesAdapter.java
index 973b243..e5a47e3 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModuleDependenciesAdapter.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModuleDependenciesAdapter.java
@@ -16,6 +16,7 @@
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -43,6 +44,7 @@
 import org.eclipse.core.resources.IWorkspaceRoot;
 import org.eclipse.core.resources.ResourcesPlugin;
 
+import org.eclipse.jface.dialogs.IDialogConstants;
 import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.jface.resource.ImageDescriptor;
 import org.eclipse.jface.viewers.TreeViewer;
@@ -57,6 +59,7 @@
 import org.eclipse.jdt.core.IPackageFragmentRoot;
 import org.eclipse.jdt.core.JavaCore;
 import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.provisional.JavaModelAccess;
 
 import org.eclipse.jdt.internal.ui.JavaPlugin;
 import org.eclipse.jdt.internal.ui.JavaPluginImages;
@@ -314,6 +317,12 @@
 			possibleTargetModules.remove(fFocusModule.getElementName());
 			ModuleAddExportsDialog dialog= new ModuleAddExportsDialog(shell, new IJavaElement[] { jContainer }, possibleTargetModules, initial);
 			if (dialog.open() == Window.OK) {
+				try {
+					moduleAttribute = ensureModuleAttribute(moduleAttribute);
+				} catch (JavaModelException e) {
+					JavaPlugin.log(e);
+					return false;
+				}
 				ModuleAddExpose expose= dialog.getExport(moduleAttribute);
 				if (expose != null) {
 					Object attribute= moduleAttribute.getValue();
@@ -354,8 +363,9 @@
 		}
 		private boolean internalRemove(List<Object> selectedElements) throws JavaModelException {
 			CPListElementAttribute moduleAttribute;
-			if (fKind == ModuleKind.System) {
-				moduleAttribute= ((CPListElement) fElem.getParentContainer()).findAttributeElement(CPListElement.MODULE);
+			Object parentContainer= fElem.getParentContainer();
+			if (parentContainer instanceof CPListElement) {
+				moduleAttribute= ((CPListElement) parentContainer).findAttributeElement(CPListElement.MODULE);
 			} else {
 				moduleAttribute= fElem.findAttributeElement(CPListElement.MODULE);
 			}
@@ -375,6 +385,9 @@
 						// need to merge details:
 						PatchModule patch= (PatchModule) node;
 						ModuleEncapsulationDetail.removePatchLocation(details, fFocusModule.getElementName(), patch.getPath());
+						if (getContextProject().equals(patch.fSource.getAncestor(IJavaElement.JAVA_PROJECT))) {
+							fDependenciesPage.unsetFocusModule(fElem);
+						}
 						patchUpdated= true;
 					} else {
 						ModuleEncapsulationDetail med= ((DetailNode<?>) node).convertToCP(moduleAttribute);
@@ -416,7 +429,7 @@
 				return false;
 			}
 		}
-		private boolean internalAddReads(Shell shell) throws JavaModelException {
+		boolean internalAddReads(Shell shell) throws JavaModelException {
 			Object container= fElem.getParentContainer();
 			CPListElement element= (container instanceof CPListElement) ? (CPListElement) container : fElem;
 			CPListElementAttribute moduleAttribute= element.findAttributeElement(CPListElement.MODULE);
@@ -435,17 +448,18 @@
 			irrelevantModules= new ArrayList<>(irrelevantModules);
 			irrelevantModules.add(fFocusModule.getElementName());
 
-			ModuleSelectionDialog dialog= ModuleSelectionDialog.forReads(shell, fElem.getJavaProject(), irrelevantModules);
+			IClasspathEntry jreEntry= fDependenciesPage.findSystemLibraryElement().getClasspathEntry();
+			ModuleSelectionDialog dialog= ModuleSelectionDialog.forReads(shell, fElem.getJavaProject(), jreEntry, irrelevantModules);
 			if (dialog.open() != 0) {				
 				return false;
 			}
-
-			if (moduleAttribute == null) {
-				// initialize missing attribute for focus module (source folder)
-				moduleAttribute= fElem.createAttributeElement(CPListElement.MODULE, new ModuleEncapsulationDetail[0], true);
+			List<IModuleDescription> result= dialog.getResult();
+			if (!handleUnavailableModulesIfNeeded(shell, result, fDependenciesPage)) {
+				return false;
 			}
 
-			List<IModuleDescription> result= dialog.getResult();
+			moduleAttribute= ensureModuleAttribute(moduleAttribute);
+
 			ModuleEncapsulationDetail[] arrayValue= null;
 			int idx= 0;
 			Object attribute= moduleAttribute.getValue();
@@ -475,11 +489,7 @@
 		private boolean internalAddPatch(Shell shell, Map<String, String> patchMap) throws JavaModelException {
 			Object container= fElem.getParentContainer();
 			CPListElement element= (container instanceof CPListElement) ? (CPListElement) container : fElem;
-			CPListElementAttribute moduleAttribute= element.findAttributeElement(CPListElement.MODULE);
-			if (moduleAttribute == null) {
-				throwNewJavaModelException("module attribute is unexpectecly missing for : "+fElem); //$NON-NLS-1$
-				return false; // not reached
-			}
+			CPListElementAttribute moduleAttribute= ensureModuleAttribute(element.findAttributeElement(CPListElement.MODULE));
 			if (!(moduleAttribute.getValue() instanceof ModuleEncapsulationDetail[])) {
 				throwNewJavaModelException("Value of module attribute has unexpected type: "+moduleAttribute.getValue()); //$NON-NLS-1$
 				return false;
@@ -531,6 +541,7 @@
 			ModuleEncapsulationDetail.addPatchLocations(detailList, fFocusModule.getElementName(), newPaths, moduleAttribute);
 			element.setAttribute(CPListElement.MODULE, detailList.toArray(new ModuleEncapsulationDetail[detailList.size()]));
 			fDependenciesPage.buildPatchMap();
+			fDependenciesPage.refreshModulesList();
 			return true;
 		}
 
@@ -550,6 +561,17 @@
 			otherAttribute.setValue(otherDetailList.toArray(new ModuleEncapsulationDetail[otherDetailList.size()]));
 			return true;
 		}
+
+		private CPListElementAttribute ensureModuleAttribute(CPListElementAttribute existing) throws JavaModelException {
+			if (existing != null) return existing;
+			if (fElem.getClasspathEntry().getEntryKind() == IClasspathEntry.CPE_SOURCE) {
+				// initialize missing attribute for focus module (source folder)
+				return fElem.createAttributeElement(CPListElement.MODULE, new ModuleEncapsulationDetail[0], true);
+			} else {
+				throwNewJavaModelException("module attribute is unexpectecly missing for : "+fElem); //$NON-NLS-1$
+				return null; // not reached
+			}
+		}
 	}
 
 	abstract static class DetailNode<D extends ModuleEncapsulationDetail> {
@@ -783,17 +805,61 @@
 		}
 	}
 
-	public static void updateButtonEnablement(TreeListDialogField<?> list, boolean enableModify, boolean enableRemove) {
+	public static void updateButtonEnablement(TreeListDialogField<?> list, boolean enableModify, boolean enableRemove, boolean enableShow) {
 		list.enableButton(IDX_REMOVE, enableRemove);
 		list.enableButton(IDX_EXPOSE_PACKAGE, enableModify);
 		list.enableButton(IDX_READ_MODULE, enableModify);
 		list.enableButton(IDX_PATCH, enableModify);
+		list.enableButton(IDX_JPMS_OPTIONS, enableShow);
 	}
 
 	static void throwNewJavaModelException(String message) throws JavaModelException {
 		throw new JavaModelException(new Status(IStatus.ERROR, JavaPlugin.getPluginId(), message));
 	}
 
+	static boolean handleUnavailableModulesIfNeeded(Shell shell, List<IModuleDescription> result, ModuleDependenciesPage masterPage) {
+		List<IModuleDescription> unavailableSystemModules= new ArrayList<>();
+		Collection<String> allModules= masterPage.getAllModules();
+		for (IModuleDescription module : result) {
+			if (!allModules.contains(module.getElementName())) {
+				if (JavaModelAccess.isSystemModule(module)) {
+					unavailableSystemModules.add(module);
+				} else {
+					IPackageFragmentRoot pfr= (IPackageFragmentRoot) module.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
+					boolean isLibrary= pfr != null && pfr.isArchive();
+					masterPage.offerSwitchToTab(shell,
+							NewWizardMessages.ModuleSelectionDialog_selectModule_title,
+							MessageFormat.format(NewWizardMessages.ModuleDependenciesAdapter_addReadsNotOnModulepath_error, module.getElementName()),
+							isLibrary);
+					return false; // the new add-reads is not yet handled
+				}
+			}
+		}
+		if (!unavailableSystemModules.isEmpty()) {
+			StringBuilder message= new StringBuilder();
+			message.append(NewWizardMessages.ModuleDependenciesAdapter_addSystemModules_question);
+			message.append(unavailableSystemModules.stream()
+					.map(IJavaElement::getElementName)
+					.sorted()
+					.collect(Collectors.joining("\n\t", "\t", ""))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+			int answer= MessageDialog.open(MessageDialog.QUESTION, shell,
+					NewWizardMessages.ModuleDependenciesAdapter_addSystemModule_title, message.toString(), SWT.NONE,
+					NewWizardMessages.ModuleDependenciesAdapter_add_button, IDialogConstants.CANCEL_LABEL);
+			if (answer == 0) {
+				try {
+					masterPage.addToSystemModules(unavailableSystemModules);
+				} catch (JavaModelException e) {
+					JavaPlugin.log(e);
+					MessageDialog.openError(shell, NewWizardMessages.ModuleDependenciesAdapter_configure_error, e.getMessage());
+					return false;
+				}
+			} else {
+				return false;
+			}
+		}
+		return true;
+	}
+
 	private final ModuleDependenciesPage fModuleDependenciesPage; // parent structure
 	private TreeListDialogField<Object> fDetailsList; // RHS widget managed by this class
 
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModuleDependenciesList.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModuleDependenciesList.java
index aeaf053..c85126b 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModuleDependenciesList.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModuleDependenciesList.java
@@ -188,9 +188,31 @@
 		return !fInitialNames.equals(fNames);
 	}
 
+	public void setFocusModule(String moduleName) {
+		fNamesComparator= new FocusAwareStringComparator(moduleName);
+		fKinds.put(fModule2Element.get(moduleName), ModuleKind.Focus);
+	}
+
+	public void unsetFocusModule(CPListElement elem) {
+		fNamesComparator= new FocusAwareStringComparator(""); //$NON-NLS-1$
+		// restore real kind:
+		Object topElem= elem.getParentContainer();
+		CPListElement topEntry= topElem instanceof CPListElement ? (CPListElement) topElem : elem;
+		ModuleKind kind= ModuleKind.Normal;
+		if (LibrariesWorkbookPage.isJREContainer(topEntry.getPath())) {
+			kind= ModuleKind.System; 
+		} else {
+			IModuleDescription module= fModules.get(elem);
+			if (module != null && module.isAutoModule()) {
+				kind= ModuleKind.Automatic;
+			}
+		}
+		fKinds.put(elem, kind);
+	}
+
 	public void refresh() {
 		fNames.sort(fNamesComparator);
-		fViewer.refresh();
+		fViewer.refresh(true, true);
 	}
 
 	public void setEnabled(boolean enable) {
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModuleDependenciesPage.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModuleDependenciesPage.java
index 0cfc571..350b55c 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModuleDependenciesPage.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModuleDependenciesPage.java
@@ -38,8 +38,11 @@
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.swt.widgets.TabItem;
 
 import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
 
 import org.eclipse.core.resources.IProject;
 
@@ -50,8 +53,6 @@
 import org.eclipse.jface.resource.ImageDescriptor;
 import org.eclipse.jface.viewers.StructuredSelection;
 
-import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer;
-
 import org.eclipse.jdt.core.IClasspathEntry;
 import org.eclipse.jdt.core.IJavaElement;
 import org.eclipse.jdt.core.IJavaProject;
@@ -75,22 +76,14 @@
 import org.eclipse.jdt.internal.ui.wizards.dialogfields.TreeListDialogField;
 
 /*
- * TODO: ("+" must have, "-" later)
+ * TODO:
  * LHS:
  * - module kind "Upgrade" of a System Library (incl. icon decoration)
- * - better help on how to remove non-JRE modules (module-info, modulepath)
- * RHS:
- * - PatchModule:
- *   - handle patch project w/o module-info (as soon as path-module is defined)
- *     - treat the patched module as the current context module (pinned)
- *   - prefer offering source folders of the context project for patch-module
  * General:
  * - distinguish test/main dependencies
  * - special elements: ALL-UNNAMED, ALL-SYSTEM ...
  * - Help pages and reference to it
  *    (add to ModuleSelectionDialog.configureShell(), ModuleDependenciesPage.getControl())
- * - Offer to switch to the corresponding tab when trying to remove a non-system module
- *    (see error scenarii in #removeModules() and also field #fPageContainer).
  */
 public class ModuleDependenciesPage extends BuildPathBasePage {
 
@@ -173,12 +166,10 @@
 	public final Map<String,String> fPatchMap= new HashMap<>();
 
 	private Control fSWTControl;
-//	private final IWorkbenchPreferenceContainer fPageContainer; // for switching page (not yet used)
 
-	public ModuleDependenciesPage(IStatusChangeListener context, CheckedListDialogField<CPListElement> classPathList, IWorkbenchPreferenceContainer pageContainer) {
+	public ModuleDependenciesPage(IStatusChangeListener context, CheckedListDialogField<CPListElement> classPathList) {
 		fClassPathList= classPathList;
 		fContext= context;
-//		fPageContainer= pageContainer;
 		fSWTControl= null;
 		
 		String[] buttonLabels= new String[] {
@@ -291,7 +282,7 @@
 			fAddSystemModuleButton.setEnabled(false);
 			fDetailsList.removeAllElements();
 			fDetailsList.refresh();
-			ModuleDependenciesAdapter.updateButtonEnablement(fDetailsList, false, false);
+			ModuleDependenciesAdapter.updateButtonEnablement(fDetailsList, false, false, false);
 			return;
 		}
 		fModuleList.setEnabled(true);
@@ -396,9 +387,9 @@
 					}
 			}
 		}
+		buildPatchMap();
 		fModuleList.captureInitial();
 		fModuleList.refresh();
-		buildPatchMap();
 	}
 
 	public Collection<String> getAllModules() {
@@ -415,6 +406,9 @@
 						ModulePatch patch= (ModulePatch) detail;
 						for (String path : patch.getPathArray()) {
 							fPatchMap.put(path, patch.fModule);
+							if (path.startsWith(fCurrJProject.getPath().toString())) {
+								fModuleList.setFocusModule(patch.fModule);
+							}
 						}
 					}
 				}
@@ -484,7 +478,7 @@
 			fDetailsList.addElement(configured);
 			fDetailsList.expandElement(configured, 1);
 		}
-		ModuleDependenciesAdapter.updateButtonEnablement(fDetailsList, elements.size() == 1, !elements.isEmpty());
+		ModuleDependenciesAdapter.updateButtonEnablement(fDetailsList, elements.size() == 1, !elements.isEmpty(), true);
 	}
 
 	@Override
@@ -501,22 +495,40 @@
     	fDetailsList.setFocus();
 	}
 
+	public void unsetFocusModule(CPListElement elem) {
+		fModuleList.unsetFocusModule(elem);
+		fModuleList.refresh();
+	}
+
 	public void refreshModulesList() {
 		fModuleList.refresh();
 	}
 
 	void addSystemModules() {
-		CPListElement cpListElement= findSystemLibraryElement();
-		ModuleSelectionDialog dialog= ModuleSelectionDialog.forSystemModules(getShell(), fCurrJProject, cpListElement.getClasspathEntry(), fModuleList.fNames, this::computeForwardClosure);
-		if (dialog.open() == IDialogConstants.OK_ID) {
-			for (IModuleDescription addedModule : dialog.getResult()) {
-				fModuleList.addModule(addedModule, getOrCreateModuleCPE(cpListElement, addedModule), ModuleKind.System);
+		try {
+			CPListElement cpListElement= findSystemLibraryElement();
+			ModuleSelectionDialog dialog= ModuleSelectionDialog.forSystemModules(getShell(), fCurrJProject, cpListElement.getClasspathEntry(), fModuleList.fNames, this::computeForwardClosure);
+			if (dialog.open() == IDialogConstants.OK_ID) {
+				List<IModuleDescription> modulesToAdd= dialog.getResult();
+				addSystemModules(cpListElement, modulesToAdd);
 			}
-			updateLimitModules(cpListElement.findAttributeElement(CPListElement.MODULE));
-			fModuleList.refresh();
+		} catch (JavaModelException e) {
+			JavaPlugin.log(e);
 		}
 	}
 
+	void addSystemModules(CPListElement cpListElement, List<IModuleDescription> modulesToAdd) {
+		for (IModuleDescription addedModule : modulesToAdd) {
+			fModuleList.addModule(addedModule, getOrCreateModuleCPE(cpListElement, addedModule), ModuleKind.System);
+		}
+		updateLimitModules(cpListElement.findAttributeElement(CPListElement.MODULE));
+		fModuleList.refresh();
+	}
+
+	public void addToSystemModules(List<IModuleDescription> modulesToAdd) throws JavaModelException {
+		addSystemModules(findSystemLibraryElement(), modulesToAdd);
+	}
+
 	CPListElement getOrCreateModuleCPE(CPListElement parentCPE, IModuleDescription module) {
 		CPListElement element= fModuleList.fModule2Element.get(module.getElementName());
 		if (element != null) {
@@ -524,18 +536,21 @@
 		}
 		try {
 			IClasspathEntry entry= fCurrJProject.getClasspathEntryFor(module.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT).getPath());
+			if (entry == null) {
+				return null;
+			}
 			return CPListElement.create(parentCPE, entry, module, true, fCurrJProject);
 		} catch (JavaModelException e) {
 			JavaPlugin.log(e);
 			return null;
 		}
 	}
-	private CPListElement findSystemLibraryElement() {
+	public CPListElement findSystemLibraryElement() throws JavaModelException {
 		for (CPListElement cpListElement : fClassPathList.getElements()) {
 			if (LibrariesWorkbookPage.isJREContainer(cpListElement.getPath()))
 				return cpListElement;
 		}
-		return null;
+		throw new JavaModelException(new Status(IStatus.ERROR, JavaPlugin.getPluginId(), "Project "+fCurrJProject.getElementName()+" has no system library"));  //$NON-NLS-1$//$NON-NLS-2$
 	}
 
 	void removeModules() {
@@ -550,9 +565,39 @@
 			}
 			IModuleDescription mod= selectedElement.getModule();
 			if (mod == null) {
-				MessageDialog.openError(getShell(), NewWizardMessages.ModuleDependenciesPage_removeModule_dialog_title,
+				// offer to switch to the suitable Modulepath tab, but need to collect some info, first:
+				IClasspathEntry selectedEntry= selectedElement.getClasspathEntry();
+				String moduleName= selectedElement.getPath().lastSegment();
+				boolean isLibrary;
+				switch (selectedEntry.getEntryKind()) {
+					case IClasspathEntry.CPE_LIBRARY:
+						for (IPackageFragmentRoot packageRoot : fCurrJProject.findPackageFragmentRoots(selectedEntry)) {
+							IModuleDescription module= packageRoot.getModuleDescription();
+							if (module == null) {
+								try {
+									module= JavaCore.getAutomaticModuleDescription(packageRoot);
+								} catch (JavaModelException | IllegalArgumentException e) {
+									// ignore
+								}
+							}
+							if (module != null) {
+								moduleName= module.getElementName();
+								break;
+							}
+						}
+						//$FALL-THROUGH$
+					case IClasspathEntry.CPE_CONTAINER:
+					case IClasspathEntry.CPE_VARIABLE:
+						isLibrary= true;
+						break;
+					default:
+						isLibrary= false;
+				}
+				offerSwitchToTab(getShell(),
+						NewWizardMessages.ModuleDependenciesPage_removeModule_dialog_title,
 						MessageFormat.format(NewWizardMessages.ModuleDependenciesPage_removeModule_error_with_hint,
-								selectedElement.getPath().lastSegment(), NewWizardMessages.ModuleDependenciesPage_removeSystemModule_error_hint));
+								moduleName, NewWizardMessages.ModuleDependenciesPage_removeSystemModule_error_hint),
+						isLibrary);
 				return;
 			}
 			String moduleName= mod.getElementName();
@@ -624,7 +669,9 @@
 	}
 	
 	private boolean confirmRemoveModule(String message) {
-		int answer= MessageDialog.open(MessageDialog.QUESTION, getShell(), NewWizardMessages.ModuleDependenciesPage_removeModule_dialog_title, message, SWT.NONE, NewWizardMessages.ModuleDependenciesPage_remove_button, NewWizardMessages.ModuleDependenciesPage_cancel_button);
+		int answer= MessageDialog.open(MessageDialog.QUESTION, getShell(),
+				NewWizardMessages.ModuleDependenciesPage_removeModule_dialog_title, message, SWT.NONE,
+				NewWizardMessages.ModuleDependenciesPage_remove_button, IDialogConstants.CANCEL_LABEL);
 		return answer == 0;
 	}
 
@@ -737,4 +784,42 @@
 	public void showJMPSOptionsDialog() {
 		new ShowJPMSOptionsDialog(getShell(), fClassPathList, allDefaultSystemModules(), this::closure, this::reduceNames).open();
 	}
+
+	public void offerSwitchToTab(Shell shell, String dialogTitle, String dialogMessage, boolean isLibrary) {
+		String tabButton= isLibrary ? NewWizardMessages.ModuleDependenciesAdapter_goToLibrariesTab_button
+				: NewWizardMessages.ModuleDependenciesAdapter_goToProjectsTab_button;
+		MessageDialog dialog= new MessageDialog(shell,
+				dialogTitle, null, dialogMessage, MessageDialog.QUESTION, 0,
+				tabButton, IDialogConstants.CANCEL_LABEL);
+		if (dialog.open() == 0) {
+			TabFolder tabFolder= (TabFolder) fSWTControl.getParent();
+			if (isLibrary) {
+				showLibrariesPage(tabFolder);
+			} else {
+				showProjectsPage(tabFolder);
+			}
+		}
+	}
+
+	void showLibrariesPage(TabFolder tabFolder) {
+		for (TabItem tabItem : tabFolder.getItems()) {
+			if (tabItem.getData() instanceof LibrariesWorkbookPage) {
+				tabFolder.setSelection(tabItem);
+				LibrariesWorkbookPage page= (LibrariesWorkbookPage) tabItem.getData();
+				page.selectRootNode(true);
+				return;
+			}
+		}
+	}
+
+	void showProjectsPage(TabFolder tabFolder) {
+		for (TabItem tabItem : tabFolder.getItems()) {
+			if (tabItem.getData() instanceof ProjectsWorkbookPage) {
+				tabFolder.setSelection(tabItem);
+				ProjectsWorkbookPage page= (ProjectsWorkbookPage) tabItem.getData();
+				page.selectRootNode(true);
+				return;
+			}
+		}
+	}
 }
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModuleEncapsulationDetail.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModuleEncapsulationDetail.java
index 3164564..d750add 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModuleEncapsulationDetail.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModuleEncapsulationDetail.java
@@ -23,8 +23,11 @@
 import java.util.stream.Collectors;
 
 import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
 
+import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRoot;
 import org.eclipse.core.resources.ResourcesPlugin;
 
 import org.eclipse.jdt.core.IClasspathAttribute;
@@ -207,6 +210,79 @@
 		public String getAttributeName() {
 			return IClasspathAttribute.PATCH_MODULE;
 		}
+
+		public String toAbsolutePathsString(IJavaProject focusProject) {
+			String[] paths= fPaths.split(File.pathSeparator);
+			String[] absPaths= new String[paths.length];
+			IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot();
+			for (int i= 0; i < paths.length; i++) {
+				IResource resource= root.findMember(new Path(paths[i]));
+				try {
+					absPaths[i]= toAbsolutePath(resource, focusProject, root);
+				} catch (JavaModelException e) {
+					JavaPlugin.log(e);
+				}
+				if (absPaths[i] == null) {
+					absPaths[i]= paths[i];
+				}
+			}
+			String allPaths= String.join(File.pathSeparator, absPaths);
+			return fModule + '=' + allPaths;
+		}
+
+		private String toAbsolutePath(IResource resource, IJavaProject focusProject, IWorkspaceRoot root) throws JavaModelException {
+			if (resource instanceof IProject) {
+				if (resource.equals(focusProject.getProject())) {
+					// focus project: collect all source locations (pre-joined into one string):
+					StringBuilder allSources= new StringBuilder();
+					for (IClasspathEntry classpathEntry : focusProject.getRawClasspath()) {
+						if (classpathEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
+							if (allSources.length() > 0) {
+								allSources.append(File.pathSeparator);
+							}
+							allSources.append(absPath(root, classpathEntry.getPath()));
+						}
+					}
+					return allSources.toString();
+				} else {
+					// other projects: use the default output locations:
+					return absPath(root, JavaCore.create((IProject) resource).getOutputLocation());
+				}
+			} else if (resource != null) {
+				if (isSourceFolderOf(resource, focusProject)) {
+					// within current project use source path:
+					return resource.getLocation().toString();
+				} else {
+					IJavaProject otherJProj= JavaCore.create(resource.getProject());
+					if (otherJProj.exists()) {
+						IClasspathEntry cpEntry= otherJProj.getClasspathEntryFor(resource.getFullPath());
+						if (cpEntry != null && cpEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
+							// source of other project -> map to its output location:
+							IPath outputLocation= cpEntry.getOutputLocation();
+							if (outputLocation == null) {
+								outputLocation= otherJProj.getOutputLocation();
+							}
+							return absPath(root, outputLocation);
+						}
+					}
+					// non-source location as-is:
+					return resource.getLocation().toString(); 
+				}
+			}
+			return null;
+		}
+
+		private String absPath(IWorkspaceRoot root, IPath path) {
+			return root.findMember(path).getLocation().toString();
+		}
+
+		private boolean isSourceFolderOf(IResource resource, IJavaProject javaProject) throws JavaModelException {
+			IPackageFragmentRoot root= javaProject.findPackageFragmentRoot(resource.getFullPath());
+			if (root != null) {
+				return root.getKind() == IPackageFragmentRoot.K_SOURCE;
+			}
+			return false;
+		}
 	}
 
 	/** Shared implementation for ModuleAddExports & ModuleAddOpens (same structure). */
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModulePatchSourceSelectionDialog.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModulePatchSourceSelectionDialog.java
index 97ea38f..b68cde9 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModulePatchSourceSelectionDialog.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModulePatchSourceSelectionDialog.java
@@ -98,7 +98,7 @@
 			if (e1.equals(fContextProject)) {
 				return -1;
 			}
-			if (e1.equals(fContextProject)) {
+			if (e2.equals(fContextProject)) {
 				return 1;
 			}
 			return super.compare(viewer, e1, e2);
@@ -151,7 +151,7 @@
 				continue;
 			}
 			try {
-				if (project.hasNature(JavaCore.NATURE_ID)) {
+				if (project.isOpen() && project.hasNature(JavaCore.NATURE_ID)) {
 					IJavaProject jProj= JavaCore.create(project);
 					if (jProj.getModuleDescription() == null) {
 						fProjects.add(jProj);
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModuleSelectionDialog.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModuleSelectionDialog.java
index b5f5297..0044493 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModuleSelectionDialog.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ModuleSelectionDialog.java
@@ -58,7 +58,9 @@
 import org.eclipse.jdt.core.IPackageFragmentRoot;
 import org.eclipse.jdt.core.JavaCore;
 import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.provisional.JavaModelAccess;
 import org.eclipse.jdt.core.search.IJavaSearchConstants;
+import org.eclipse.jdt.core.search.IJavaSearchScope;
 import org.eclipse.jdt.core.search.SearchEngine;
 import org.eclipse.jdt.core.search.SearchMatch;
 import org.eclipse.jdt.core.search.SearchParticipant;
@@ -66,6 +68,7 @@
 import org.eclipse.jdt.core.search.SearchRequestor;
 
 import org.eclipse.jdt.internal.ui.JavaPlugin;
+import org.eclipse.jdt.internal.ui.search.JavaSearchScopeFactory;
 import org.eclipse.jdt.internal.ui.wizards.NewWizardMessages;
 import org.eclipse.jdt.internal.ui.wizards.buildpaths.ModuleDependenciesList.ModuleKind;
 import org.eclipse.jdt.internal.ui.wizards.buildpaths.ModuleDependenciesList.ModulesLabelProvider;
@@ -83,10 +86,7 @@
 	private SelectionButtonDialogField fSelectAllCheckbox;
 	
 	boolean fInSetSelection= false; // to avoid re-entrance -> StackOverflow
-
-	// input data:
-	private IJavaProject fJavaProject;
-	private IClasspathEntry fJREEntry;
+	boolean fWaitingForSearch= false;
 
 	// internal storage and a client-provided function:
 	private Set<String> fAllIncluded; 		// transitive closure over modules already shown
@@ -108,43 +108,45 @@
 	 * @return the configured dialog
 	 */
 	public static ModuleSelectionDialog forSystemModules(Shell shell, IJavaProject javaProject, IClasspathEntry jreEntry, List<String> shownModules, Function<List<String>, Set<String>> closureComputation) {
-		return new ModuleSelectionDialog(shell, javaProject, jreEntry, shownModules, closureComputation,
+		return new ModuleSelectionDialog(shell, javaProject, jreEntry, false, shownModules, closureComputation,
 				NewWizardMessages.ModuleSelectionDialog_addSystemModules_title, NewWizardMessages.ModuleSelectionDialog_addSystemModules_message);
 	}
 	/**
 	 * Let the user select a module from all modules found in the workspace, except those in {@code irrelevantModules}.
 	 * @param shell for showing the dialog
 	 * @param javaProject the java project whose build path is being configured
+	 * @param jreEntry a classpath entry representing the JRE system library
 	 * @param irrelevantModules list of modules not relevant for selection
 	 * @return the configured dialog
 	 */
-	public static ModuleSelectionDialog forReads(Shell shell, IJavaProject javaProject, List<String> irrelevantModules) {
-		return new ModuleSelectionDialog(shell, javaProject, null, irrelevantModules, HashSet::new,
+	public static ModuleSelectionDialog forReads(Shell shell, IJavaProject javaProject, IClasspathEntry jreEntry, List<String> irrelevantModules) {
+		return new ModuleSelectionDialog(shell, javaProject, jreEntry, true, irrelevantModules, HashSet::new,
 				NewWizardMessages.ModuleSelectionDialog_selectModule_title, NewWizardMessages.ModuleSelectionDialog_selectReadModule_message);
 	}
-	private ModuleSelectionDialog(Shell shell, IJavaProject javaProject, IClasspathEntry jreEntry, List<String> shownModules, 
-			Function<List<String>, Set<String>> closureComputation, String title, String message) {
+	private ModuleSelectionDialog(Shell shell, IJavaProject javaProject, IClasspathEntry jreEntry, boolean searchWorkspace,
+			List<String> shownModules, Function<List<String>, Set<String>> closureComputation, String title, String message) {
 		super(shell);
 		fTitle= title;
 		fMessage= message;
-		fJavaProject= javaProject;
-		fJREEntry= jreEntry;
 		fAllIncluded= closureComputation.apply(shownModules);
 		fClosureComputation= closureComputation;
-		if (jreEntry != null) {  // searching only modules from this JRE entry (quick)
+		if (jreEntry != null) {  // find system modules from this JRE entry (quick)
 			Set<String> result= new HashSet<>();
-			for (IPackageFragmentRoot root : fJavaProject.findUnfilteredPackageFragmentRoots(fJREEntry)) {
+			for (IPackageFragmentRoot root : javaProject.findUnfilteredPackageFragmentRoots(jreEntry)) {
 				checkAddModule(result, root.getModuleDescription());
 			}
 			List<String> list= new ArrayList<>(result);
 			list.sort(String::compareTo);
 			fAvailableModules= list;
-		} else {  // searching all modules in the workspace (slow)
+		}
+		if (searchWorkspace) {  // searching all modules in the workspace (slow)
+			fWaitingForSearch= true;
 			new Job(NewWizardMessages.ModuleSelectionDialog_searchModules_job) {
 				@Override
 				public IStatus run(IProgressMonitor monitor) {
 					try {
-						fAvailableModules= searchAvailableModules(monitor);
+						fAvailableModules.addAll(searchAvailableModules(monitor));
+						fAvailableModules.sort(String::compareTo);
 						if (getReturnCode() == Window.CANCEL) {
 							return Status.CANCEL_STATUS;
 						}
@@ -172,17 +174,21 @@
 			public void acceptSearchMatch(SearchMatch match) throws CoreException {
 				Object element= match.getElement();
 				if (element instanceof IModuleDescription) {
-					checkAddModule(result, (IModuleDescription) element);
+					IModuleDescription module= (IModuleDescription) element;
+					if (!JavaModelAccess.isSystemModule(module))
+						checkAddModule(result, module);
 				}
 			}
 		};
 		SearchParticipant[] participants= new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() };
-		new SearchEngine().search(pattern, participants, SearchEngine.createWorkspaceScope(), requestor, monitor);
+		IJavaSearchScope scope= JavaSearchScopeFactory.getInstance().createWorkspaceScope(false); // skip JRE modules, which are found directly via the jreEntry
+		new SearchEngine().search(pattern, participants, scope, requestor, monitor);
 		if (getReturnCode() == Window.CANCEL) { // should cancelPressed() actively abort the search?
 			return Collections.emptyList();
 		}
 		// also search for automatic modules:
 		for (IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects()) {
+			if (!project.isOpen()) continue;
 			IJavaProject jPrj= JavaCore.create(project);
 			if (jPrj.getModuleDescription() == null) {
 				checkAddModule(result, JavaCore.getAutomaticModuleDescription(jPrj));
@@ -195,9 +201,7 @@
 				}
 			}
 		}
-		List<String> list= new ArrayList<>(result);
-		list.sort(String::compareTo);
-		return list;
+		return new ArrayList<>(result);
 	}
 	
 	boolean isJREChild(IJavaProject jPrj, IPackageFragmentRoot root) {
@@ -275,15 +279,17 @@
 		gd.heightHint= converter.convertHeightInCharsToPixels(20);
 		tableViewer.getControl().setLayoutData(gd);
 
-		if (fAvailableModules == null) {
+		if (fWaitingForSearch) {
 			message.setText(NewWizardMessages.ModuleSelectionDialog_searchModules_temp_message);
 			fFlipMessage= () ->  {
 				message.setText(fMessage);
 			};
 		} else {
-			tableViewer.setInput(fAvailableModules);
 			message.setText(fMessage);
 		}
+		if (fAvailableModules != null) {
+			tableViewer.setInput(fAvailableModules);			
+		}
 		fViewer= tableViewer;
 		
 		fSelectAllCheckbox= new SelectionButtonDialogField(SWT.CHECK);
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ProjectsWorkbookPage.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ProjectsWorkbookPage.java
index 2facc8c..acdb4d8 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ProjectsWorkbookPage.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ProjectsWorkbookPage.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2018 IBM Corporation and others.
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -785,4 +785,8 @@
 	public void setFocus() {
     	fProjectsList.setFocus();
     }
+
+	public void selectRootNode(boolean modulePath) {
+		selectRootNode(fProjectsList, modulePath);
+	}
 }
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ShowJPMSOptionsDialog.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ShowJPMSOptionsDialog.java
index a94faad..00faf2f 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ShowJPMSOptionsDialog.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/ShowJPMSOptionsDialog.java
@@ -30,6 +30,7 @@
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.swt.widgets.Text;
 
@@ -46,6 +47,7 @@
 
 import org.eclipse.jdt.internal.ui.JavaPlugin;
 import org.eclipse.jdt.internal.ui.wizards.NewWizardMessages;
+import org.eclipse.jdt.internal.ui.wizards.buildpaths.ModuleEncapsulationDetail.ModulePatch;
 import org.eclipse.jdt.internal.ui.wizards.dialogfields.ListDialogField;
 
 /**
@@ -102,20 +104,25 @@
 	protected Control createDialogArea(Composite parent) {
 		Composite comp= (Composite) super.createDialogArea(parent);
 		Font font= parent.getFont();
+		int widthHint= convertWidthInCharsToPixels(60);
+
+		Label message= new Label(comp, SWT.LEFT + SWT.WRAP);
+		message.setText(NewWizardMessages.ShowJPMSOptionsDialog_explanation_label);
+		GridData gdLabel= new GridData(SWT.FILL, SWT.NONE, true, false);
+		gdLabel.widthHint= widthHint;
+		message.setLayoutData(gdLabel);
 
 		Group group= new Group(comp, SWT.NONE);
 		GridLayout topLayout= new GridLayout();
 		group.setLayout(topLayout);
 		GridData gd= new GridData(GridData.FILL_BOTH);
 		gd.heightHint= convertHeightInCharsToPixels(15);
-		gd.widthHint= convertWidthInCharsToPixels(50);
+		gd.widthHint= widthHint;
 		group.setLayoutData(gd);
 		group.setFont(font);
 
 		fJPMSModuleOptionsText= new Text(group, SWT.MULTI | SWT.WRAP | SWT.BORDER | SWT.V_SCROLL);
 		gd= new GridData(GridData.FILL_BOTH);
-		gd.heightHint= convertHeightInCharsToPixels(10);
-		gd.widthHint= convertWidthInCharsToPixels(60);
 		fJPMSModuleOptionsText.setLayoutData(gd);
 
 		String command= getOptions();
@@ -158,9 +165,10 @@
 						case IClasspathAttribute.ADD_EXPORTS:
 						case IClasspathAttribute.ADD_OPENS:
 						case IClasspathAttribute.ADD_READS:
-							for (String value : detail.toString().split(COMMA)) {
-								buf.append(OPTION_START).append(optName).append(BLANK).append(value).append(BLANK);
-							}
+							buf.append(OPTION_START).append(optName).append(BLANK).append(detail.toString()).append(BLANK);
+							break;
+						case IClasspathAttribute.PATCH_MODULE:
+							buf.append(OPTION_START).append(optName).append(BLANK).append(((ModulePatch) detail).toAbsolutePathsString(cpElement.getJavaProject())).append(BLANK);
 							break;
 						case IClasspathAttribute.LIMIT_MODULES:
 							addLimitModules(buf, systemLibrary.getJavaProject(), detail.toString());