Bug 563412 - Port Settings inconsistently saved/restored for Docker Run

- add new IRunDockerImageLaunchConfigurationConstants.UNUSED_PORTS
- modify ImageRun.getDockerHostConfig() to take a list argument
  that will be modified to contain all unselected ports
- fix ExposedPortModel.fromString() method to not include the
  slash separator as part of the type
- modify LaunchConfigurationUtils.createRunImageLaunchConfiguration
  method to accept new unused ports list argument
- modify RunDockerImageLaunchConfigurationDelegate to pass an
  empty unused ports list to RunImageCommandHandler.runImage()
- modify RunImageCommandHandler.execute() to use new API for
  getting host config and setting unused ports list
- modify RunImagePortsTab.initializeFrom() method to get unused ports
  from launch configuration
- fix Docker ui tests to pass empty unused ports list
- remove checkAllElements() in ImageRunSelectionPage and let
  selection be determined dynamically from settings
- fix ImageRunSelectionPage.setDefaultValues() to restore
  unused ports

Change-Id: I0eda891c30334604cfe59511e553448540ac1f88
Reviewed-on: https://git.eclipse.org/r/163348
Tested-by: Linux Tools Bot <linuxtools-bot@eclipse.org>
Reviewed-by: Jeff Johnston <jjohnstn@redhat.com>
diff --git a/containers/org.eclipse.linuxtools.docker.ui.tests/src/org/eclipse/linuxtools/internal/docker/ui/wizards/EditDockerConnectionSWTBotTest.java b/containers/org.eclipse.linuxtools.docker.ui.tests/src/org/eclipse/linuxtools/internal/docker/ui/wizards/EditDockerConnectionSWTBotTest.java
index 31945e3..401acde 100644
--- a/containers/org.eclipse.linuxtools.docker.ui.tests/src/org/eclipse/linuxtools/internal/docker/ui/wizards/EditDockerConnectionSWTBotTest.java
+++ b/containers/org.eclipse.linuxtools.docker.ui.tests/src/org/eclipse/linuxtools/internal/docker/ui/wizards/EditDockerConnectionSWTBotTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2015, 2019 Red Hat.
+ * Copyright (c) 2015, 2020 Red Hat.
  *
  * This program and the accompanying materials are made
  * available under the terms of the Eclipse Public License 2.0
@@ -17,6 +17,7 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.concurrent.TimeUnit;
 
 import org.eclipse.core.resources.IResource;
@@ -148,7 +149,8 @@
 		final IDockerHostConfig hostConfig = MockDockerHostConfigFactory.publishAllPorts(true).networkMode(networkMode)
 				.build();
 		final ILaunchConfiguration runImageLaunchConfiguration = LaunchConfigurationUtils
-				.createRunImageLaunchConfiguration(image, containerConfig, hostConfig, "some_container", false);
+				.createRunImageLaunchConfiguration(image, containerConfig, hostConfig, new ArrayList<String>(),
+						"some_container", false);
 		return runImageLaunchConfiguration.getName();
 	}
 
diff --git a/containers/org.eclipse.linuxtools.docker.ui.tests/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ImageRunSWTBotTest.java b/containers/org.eclipse.linuxtools.docker.ui.tests/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ImageRunSWTBotTest.java
index 0cb1594..c721e1e 100644
--- a/containers/org.eclipse.linuxtools.docker.ui.tests/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ImageRunSWTBotTest.java
+++ b/containers/org.eclipse.linuxtools.docker.ui.tests/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ImageRunSWTBotTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2015, 2018 Red Hat.
+ * Copyright (c) 2015, 2020 Red Hat.
  *
  * This program and the accompanying materials are made
  * available under the terms of the Eclipse Public License 2.0
@@ -15,6 +15,7 @@
 
 import static org.assertj.core.api.Assertions.assertThat;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.concurrent.TimeUnit;
 
@@ -76,7 +77,8 @@
 		final IDockerHostConfig hostConfig = MockDockerHostConfigFactory.publishAllPorts(true).networkMode(networkMode)
 				.build();
 		final ILaunchConfiguration runImageLaunchConfiguration = LaunchConfigurationUtils
-				.createRunImageLaunchConfiguration(image, containerConfig, hostConfig, "some_container", false);
+				.createRunImageLaunchConfiguration(image, containerConfig, hostConfig, new ArrayList<String>(),
+						"some_container", false);
 		return runImageLaunchConfiguration.getName();
 	}
 
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/commands/RunImageCommandHandler.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/commands/RunImageCommandHandler.java
index e987ea9..449765a 100644
--- a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/commands/RunImageCommandHandler.java
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/commands/RunImageCommandHandler.java
@@ -1,6 +1,6 @@
 /*******************************************************************************
- * Copyright (c) 2014, 2018 Red Hat Inc. and others.
- * 
+ * Copyright (c) 2014, 2020 Red Hat Inc. and others.
+ *
  * This program and the accompanying materials are made
  * available under the terms of the Eclipse Public License 2.0
  * which is available at https://www.eclipse.org/legal/epl-2.0/
@@ -16,6 +16,8 @@
 import static org.eclipse.linuxtools.internal.docker.ui.commands.CommandUtils.getRunConsole;
 
 import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
 
 import org.eclipse.core.commands.AbstractHandler;
 import org.eclipse.core.commands.ExecutionEvent;
@@ -70,10 +72,11 @@
 				if (runImage) {
 					final IDockerContainerConfig containerConfig = wizard
 							.getDockerContainerConfig();
+					List<String> unusedPorts = new ArrayList<>();
 					final IDockerHostConfig hostConfig = wizard
-							.getDockerHostConfig();
+							.getDockerHostConfig(unusedPorts);
 					runImage(selectedImage, containerConfig, null,
-							hostConfig, wizard.getDockerContainerName(),
+							hostConfig, unusedPorts, wizard.getDockerContainerName(),
 							wizard.removeWhenExits());
 				}
 			} catch (DockerException | CoreException e) {
@@ -85,7 +88,7 @@
 
 	/**
 	 * Run the given {@link IDockerImage} with the given settings
-	 * 
+	 *
 	 * @param image
 	 * @param containerConfig
 	 * @param hostConfig
@@ -95,7 +98,9 @@
 	public static void runImage(final IDockerImage image,
 			final IDockerContainerConfig containerConfig,
 			final ILaunch launch,
-			final IDockerHostConfig hostConfig, final String containerName,
+			final IDockerHostConfig hostConfig,
+			final List<String> unusedPorts,
+			final String containerName,
 			final boolean removeWhenExits) {
 		final IDockerConnection connection = image.getConnection();
 		if (containerConfig.tty()) {
@@ -164,7 +169,7 @@
 					// create a launch configuration from the container
 					LaunchConfigurationUtils.createRunImageLaunchConfiguration(image,
 							containerConfig,
-							hostConfig, containerName,
+							hostConfig, unusedPorts, containerName,
 							removeWhenExits);
 				} catch (final DockerException | InterruptedException e) {
 					if (console != null) {
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/launch/IRunDockerImageLaunchConfigurationConstants.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/launch/IRunDockerImageLaunchConfigurationConstants.java
index 62e7eb7..e244dca 100644
--- a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/launch/IRunDockerImageLaunchConfigurationConstants.java
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/launch/IRunDockerImageLaunchConfigurationConstants.java
@@ -1,6 +1,6 @@
 /*******************************************************************************
- * Copyright (c) 2015, 2018 Red Hat.
- * 
+ * Copyright (c) 2015, 2020 Red Hat.
+ *
  * This program and the accompanying materials are made
  * available under the terms of the Eclipse Public License 2.0
  * which is available at https://www.eclipse.org/legal/epl-2.0/
@@ -42,6 +42,8 @@
 
 	String PUBLISHED_PORTS = "publishedPorts"; //$NON-NLS-1$
 
+	String UNUSED_PORTS = "unusedPorts"; //$NON-NLS-1$
+
 	String LINKS = "links"; //$NON-NLS-1$
 
 	String INTERACTIVE = "interactive"; //$NON-NLS-1$
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/launch/LaunchConfigurationUtils.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/launch/LaunchConfigurationUtils.java
index dac3551..d1157fa 100644
--- a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/launch/LaunchConfigurationUtils.java
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/launch/LaunchConfigurationUtils.java
@@ -1,6 +1,6 @@
 /*******************************************************************************
- * Copyright (c) 2015,2018 Red Hat.
- * 
+ * Copyright (c) 2015, 2020 Red Hat.
+ *
  * This program and the accompanying materials are made
  * available under the terms of the Eclipse Public License 2.0
  * which is available at https://www.eclipse.org/legal/epl-2.0/
@@ -36,6 +36,7 @@
 import static org.eclipse.linuxtools.internal.docker.ui.launch.IRunDockerImageLaunchConfigurationConstants.PUBLISHED_PORTS;
 import static org.eclipse.linuxtools.internal.docker.ui.launch.IRunDockerImageLaunchConfigurationConstants.PUBLISH_ALL_PORTS;
 import static org.eclipse.linuxtools.internal.docker.ui.launch.IRunDockerImageLaunchConfigurationConstants.READONLY;
+import static org.eclipse.linuxtools.internal.docker.ui.launch.IRunDockerImageLaunchConfigurationConstants.UNUSED_PORTS;
 
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -107,7 +108,7 @@
 	/**
 	 * Creates a new {@link ILaunchConfiguration} for the given
 	 * {@link IDockerContainer}.
-	 * 
+	 *
 	 * @param baseConfigurationName
 	 *            the base configuration name to use when creating the
 	 *            {@link ILaunchConfiguration}.
@@ -123,12 +124,14 @@
 	 * @param removeWhenExits
 	 *            flag to indicate if container should be removed when exited
 	 * @return the generated {@link ILaunchConfiguration}
-	 * 
+	 *
 	 */
 	public static ILaunchConfiguration createRunImageLaunchConfiguration(
 			final IDockerImage image,
 			final IDockerContainerConfig containerConfig,
-			final IDockerHostConfig hostConfig, final String containerName,
+			final IDockerHostConfig hostConfig,
+			final List<String> unusedPorts,
+			final String containerName,
 			final boolean removeWhenExits) {
 		try {
 			final ILaunchManager manager = DebugPlugin.getDefault()
@@ -175,6 +178,7 @@
 			} else {
 				workingCopy.setAttribute(PUBLISHED_PORTS,
 						serializePortBindings(hostConfig.portBindings()));
+				workingCopy.setAttribute(UNUSED_PORTS, unusedPorts);
 			}
 			// links (with format being: "<containerName>:<containerAlias>")
 			workingCopy.setAttribute(LINKS, hostConfig.links());
@@ -219,7 +223,7 @@
 			if (hostConfig.networkMode() != null)
 				workingCopy.setAttribute(NETWORK_MODE,
 						hostConfig.networkMode());
-				
+
 			// resources limitations
 			if (containerConfig.memory() != null) {
 				workingCopy.setAttribute(ENABLE_LIMITS, true);
@@ -250,7 +254,7 @@
 	/**
 	 * Serializes the given Port Bindings to save them in an
 	 * {@link ILaunchConfiguration}
-	 * 
+	 *
 	 * @param bindings
 	 * @return a {@link List} of port bindings serialized in the following
 	 *         format:
@@ -283,7 +287,7 @@
 	/**
 	 * Serializes the given Port Bindings to save them in an
 	 * {@link ILaunchConfiguration}
-	 * 
+	 *
 	 * @param bindings
 	 * @return a {@link List} of port bindings serialized in the following
 	 *         format:
@@ -315,7 +319,7 @@
 	/**
 	 * Deserializes the given Port Bindings to use them in an
 	 * {@link ILaunchConfiguration}
-	 * 
+	 *
 	 * @param bindings
 	 *            a {@link List} of serialized bindings
 	 * @return a {@link Map} of {@link List} of {@link IDockerPortBinding}
@@ -347,7 +351,7 @@
 
 	/**
 	 * Computes the size of the given {@link Button} and returns the width.
-	 * 
+	 *
 	 * @param button
 	 *            the button for which the size must be computed.
 	 * @return the width hint for the given button
@@ -364,7 +368,7 @@
 	/**
 	 * Looks-up the {@link ILaunchConfiguration} with the given type and
 	 * <strong>IDockerImage's name</strong>.
-	 * 
+	 *
 	 * @param type
 	 *            the configuration type
 	 * @param imageName
@@ -382,7 +386,7 @@
 	/**
 	 * Looks-up the {@link ILaunchConfiguration} with the given type and
 	 * <strong>IDockerImage's name</strong>.
-	 * 
+	 *
 	 * @param type
 	 *            the configuration type
 	 * @param imageName
@@ -417,7 +421,7 @@
 	/**
 	 * Returns the {@link ILaunchConfigurationWorkingCopy} with the given type
 	 * and <strong>IDockerImage's name</strong>.
-	 * 
+	 *
 	 * @param type
 	 *            the configuration type
 	 * @param imageName
@@ -447,7 +451,7 @@
 	/**
 	 * Looks-up the {@link ILaunchConfiguration} with the given type and
 	 * <strong>name</strong>.
-	 * 
+	 *
 	 * @param type
 	 *            the configuration type
 	 * @param name
@@ -473,7 +477,7 @@
 	/**
 	 * Converts the given <code>path</code> to a Unix path if the current OS if
 	 * {@link Platform#OS_WIN32}
-	 * 
+	 *
 	 * @param path
 	 *            the path to convert
 	 * @return the converted path or the given path, depending on the current
@@ -490,7 +494,7 @@
 	/**
 	 * Converts the given path to a portable form, replacing all "\" and ": "
 	 * with "/" if the given <code>os</code> is {@link Platform#OS_WIN32}.
-	 * 
+	 *
 	 * @param os
 	 *            the current OS
 	 * @param path
@@ -519,12 +523,12 @@
 	/**
 	 * Converts the given <code>path</code> back to a Windows path if the
 	 * current OS if {@link Platform#OS_WIN32}.
-	 * 
+	 *
 	 * <p>
 	 * Note: This is the revert operation of
 	 * {@link LaunchConfigurationUtils#convertToUnixPath(String)}.
 	 * </p>
-	 * 
+	 *
 	 * @param path
 	 *            the path to convert
 	 * @return the converted path or the given path, depending on the current
@@ -541,7 +545,7 @@
 	/**
 	 * Converts the given path to a portable form, replacing all "\" and ": "
 	 * with "/" if the given <code>os</code> is {@link Platform#OS_WIN32}.
-	 * 
+	 *
 	 * @param os
 	 *            the current OS
 	 * @param path
@@ -569,7 +573,7 @@
 	/**
 	 * Creates a Launch Configuration name from the given repoName or from the
 	 * given resource's project if the repoName was <code>null</code>.
-	 * 
+	 *
 	 * @param imageName
 	 *            the full image name
 	 * @param resource
@@ -607,7 +611,7 @@
 	/**
 	 * Creates a new {@link ILaunchConfiguration} to build an
 	 * {@link IDockerImage} from a Dockerfile {@link IResource}.
-	 * 
+	 *
 	 * @param connection
 	 *            the connection to use to submit the build
 	 * @param repoName
@@ -644,7 +648,7 @@
 	/**
 	 * Creates an {@link ILaunchConfiguration} for to run the
 	 * {@code docker-compose up} command.
-	 * 
+	 *
 	 * @param connection
 	 *            the Docker connection to use
 	 * @param dockerComposeScript
@@ -681,7 +685,7 @@
 	/**
 	 * Creates a Launch Configuration name from the given
 	 * {@code dockerComposeScript}.
-	 * 
+	 *
 	 * @param dockerComposeScript
 	 *            the name of the {@code Docker Compose} script
 	 * @return the {@link ILaunchConfiguration} name
@@ -696,7 +700,7 @@
 	 * Updates all {@link ILaunchConfiguration} of the given {@code type} where
 	 * there is an attribute with the given {@code attributeName} of the given
 	 * {@code oldValue}, and sets the {@code newValue} instead.
-	 * 
+	 *
 	 * @param type
 	 *            the type of {@link ILaunchConfiguration} to find
 	 * @param attributeName
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/launch/RunDockerImageLaunchConfigurationDelegate.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/launch/RunDockerImageLaunchConfigurationDelegate.java
index effc99b..336fddd 100644
--- a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/launch/RunDockerImageLaunchConfigurationDelegate.java
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/launch/RunDockerImageLaunchConfigurationDelegate.java
@@ -1,6 +1,6 @@
 /*******************************************************************************
- * Copyright (c) 2015,2018 Red Hat.
- * 
+ * Copyright (c) 2015, 2020 Red Hat.
+ *
  * This program and the accompanying materials are made
  * available under the terms of the Eclipse Public License 2.0
  * which is available at https://www.eclipse.org/legal/epl-2.0/
@@ -70,6 +70,8 @@
 					launch,
 					hostConfig,
 					config.getAttribute(
+							IRunDockerImageLaunchConfigurationConstants.UNUSED_PORTS, new ArrayList<String>()),
+					config.getAttribute(
 							IRunDockerImageLaunchConfigurationConstants.CONTAINER_NAME,
 							(String) null),
 					config.getAttribute(
@@ -106,7 +108,7 @@
 					.deserializePortBindings(serializedBindings);
 			hostConfigBuilder.portBindings(portBindings);
 		}
-		
+
 		// container links
 		final List<String> links = config.getAttribute(
 				IRunDockerImageLaunchConfigurationConstants.LINKS,
@@ -128,10 +130,10 @@
 				bind.append(",ro"); //$NON-NLS-1$
 			}
 			binds.add(bind.toString());
-					
+
 		}
 		hostConfigBuilder.binds(binds);
-		
+
 		// run in privileged mode
 		final boolean privileged = config.getAttribute(
 				IRunDockerImageLaunchConfigurationConstants.PRIVILEGED,
@@ -188,7 +190,7 @@
 				IRunDockerImageLaunchConfigurationConstants.ENV_VARIABLES,
 				new ArrayList<String>());
 		config.env(environmentVariables);
-		
+
 		// labels
 		final Map<String, String> labelVariables = lconfig.getAttribute(
 				IRunDockerImageLaunchConfigurationConstants.LABELS,
@@ -251,7 +253,7 @@
 	/**
 	 * Show a selection dialog that allows the user to choose one of the
 	 * connections to use to build the Image.
-	 * 
+	 *
 	 * @param connections
 	 *            Array of connections.
 	 * @return The chosen connection, or <code>null</code> if the user cancelled
@@ -283,7 +285,7 @@
 
 	/**
 	 * Get the active Workbench shell.
-	 * 
+	 *
 	 * @return active shell as returned by the plug-in
 	 */
 	protected Shell getActiveWorkbenchShell() {
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/launch/RunImagePortsTab.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/launch/RunImagePortsTab.java
index 9645db8..eb5d590 100644
--- a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/launch/RunImagePortsTab.java
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/launch/RunImagePortsTab.java
@@ -1,6 +1,6 @@
 /*******************************************************************************
- * Copyright (c) 2015, 2019 Red Hat Inc. and others.
- * 
+ * Copyright (c) 2015, 2020 Red Hat Inc. and others.
+ *
  * This program and the accompanying materials are made
  * available under the terms of the Eclipse Public License 2.0
  * which is available at https://www.eclipse.org/legal/epl-2.0/
@@ -339,6 +339,8 @@
 			final List<String> publishedPorts = configuration.getAttribute(
 					IRunDockerImageLaunchConfigurationConstants.PUBLISHED_PORTS,
 					new ArrayList<String>());
+			final List<String> unusedPorts = configuration
+					.getAttribute(IRunDockerImageLaunchConfigurationConstants.UNUSED_PORTS, new ArrayList<String>());
 			final Set<ExposedPortModel> selectedPorts = new HashSet<>();
 			for (String port : publishedPorts) {
 				final ImageRunSelectionModel.ExposedPortModel exposedPort = ImageRunSelectionModel.ExposedPortModel
@@ -348,6 +350,13 @@
 					selectedPorts.add(exposedPort);
 				}
 			}
+			for (String port : unusedPorts) {
+				final ImageRunSelectionModel.ExposedPortModel exposedPort = ImageRunSelectionModel.ExposedPortModel
+						.createPortModel(port);
+				exposedPort.setSelected(false);
+				model.addExposedPort(exposedPort);
+			}
+
 			// select ports
 			model.setSelectedPorts(selectedPorts);
 
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ImageRun.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ImageRun.java
index e265830..400954d 100644
--- a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ImageRun.java
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ImageRun.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014, 2018 Red Hat.
+ * Copyright (c) 2014, 2020 Red Hat.
  *
  * This program and the accompanying materials are made
  * available under the terms of the Eclipse Public License 2.0
@@ -126,7 +126,7 @@
 		return this.imageRunSelectionPage.getModel().isRemoveWhenExits();
 	}
 
-	public IDockerHostConfig getDockerHostConfig() {
+	public IDockerHostConfig getDockerHostConfig(List<String> unusedPorts) {
 		final ImageRunSelectionModel selectionModel = this.imageRunSelectionPage
 				.getModel();
 		final ImageRunResourceVolumesVariablesModel resourcesModel = this.imageRunResourceVolumesPage
@@ -135,6 +135,7 @@
 				.getModel();
 
 		final DockerHostConfig.Builder hostConfigBuilder = new DockerHostConfig.Builder();
+
 		if (selectionModel.isPublishAllPorts()) {
 			hostConfigBuilder.publishAllPorts(true);
 		} else {
@@ -142,6 +143,17 @@
 			for (ExposedPortModel exposedPort : selectionModel.getExposedPorts()) {
 				// only selected Ports in the CheckboxTableViewer are exposed.
 				if (!selectionModel.getSelectedPorts().contains(exposedPort)) {
+					StringBuilder b = new StringBuilder();
+					b.append(exposedPort.getContainerPort());
+					b.append("/");
+					b.append(exposedPort.getPortType());
+					b.append(":"); //$NON-NLS-1$
+					if (exposedPort.getHostAddress() != null) {
+						b.append(exposedPort.getHostAddress());
+					}
+					b.append(":"); //$NON-NLS-1$
+					b.append(exposedPort.getHostPort());
+					unusedPorts.add(b.toString());
 					continue;
 				}
 				final DockerPortBinding portBinding = new DockerPortBinding(exposedPort.getHostAddress(),
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ImageRunSelectionModel.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ImageRunSelectionModel.java
index 94af1f5..22e5a2b 100644
--- a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ImageRunSelectionModel.java
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ImageRunSelectionModel.java
@@ -1,6 +1,6 @@
 /*******************************************************************************
- * Copyright (c) 2014, 2018 Red Hat.
- * 
+ * Copyright (c) 2014, 2020 Red Hat.
+ *
  * This program and the accompanying materials are made
  * available under the terms of the Eclipse Public License 2.0
  * which is available at https://www.eclipse.org/legal/epl-2.0/
@@ -33,7 +33,7 @@
 
 /**
  * Databinding model for the {@link ImageRunSelectionPage}
- * 
+ *
  * @author xcoulon
  *
  */
@@ -373,7 +373,7 @@
 
 	/**
 	 * Set the container links
-	 * 
+	 *
 	 * @param links
 	 *            in the following format:
 	 *            <code>&lt;containerName&gt;:&lt;containerAlias&gt;</code>
@@ -477,7 +477,7 @@
 		/**
 		 * Parses and converts the {@link List} of the given {@link String}
 		 * values into a {@link List} of {@link ExposedPortModel}
-		 * 
+		 *
 		 * @param exposedPortInfos
 		 *            the input values
 		 * @return the corresponding {@link ExposedPortModel}s
@@ -498,7 +498,7 @@
 		/**
 		 * Parse the given value and returns an instance of
 		 * {@link ExposedPortModel}.
-		 * 
+		 *
 		 * @param exposedPortInfo
 		 *            the value to parse
 		 * @return the corresponding {@link ExposedPortModel}
@@ -512,7 +512,7 @@
 			if (firstColumnSeparator == -1 && exposedPortInfo
 					.indexOf(CONTAINER_TYPE_SEPARATOR) != -1) {
 				final String type = exposedPortInfo.substring(
-						exposedPortInfo.indexOf(CONTAINER_TYPE_SEPARATOR)); // $NON-NLS-1$
+						exposedPortInfo.indexOf(CONTAINER_TYPE_SEPARATOR) + 1); // $NON-NLS-1$
 				final ExposedPortModel exposedPort = new ExposedPortModel(
 						privatePort, type, "", privatePort); // $NON-NLS-1$
 				return exposedPort; // $NON-NLS-1$
@@ -521,7 +521,8 @@
 						.indexOf(SEPARATOR,
 						firstColumnSeparator + 1);
 				final String type = exposedPortInfo.substring(
-						exposedPortInfo.indexOf(CONTAINER_TYPE_SEPARATOR), // $NON-NLS-1$
+						exposedPortInfo.indexOf(CONTAINER_TYPE_SEPARATOR)
+								+ 1, // $NON-NLS-1$
 						firstColumnSeparator); // $NON-NLS-1$
 				final String hostIP = exposedPortInfo.substring(
 						firstColumnSeparator + 1, secondColumnSeparator);
@@ -535,7 +536,7 @@
 
 		/**
 		 * Full constructor
-		 * 
+		 *
 		 * @param privatePort
 		 * @param portType
 		 * @param hostAddress
@@ -554,7 +555,7 @@
 
 		/**
 		 * Create an ExposedPortModel from its toString output
-		 * 
+		 *
 		 * @param stringValue
 		 * @return ExposedPortModel
 		 */
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ImageRunSelectionPage.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ImageRunSelectionPage.java
index 5637d95..16595a6 100644
--- a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ImageRunSelectionPage.java
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ImageRunSelectionPage.java
@@ -1,6 +1,6 @@
 /*******************************************************************************
- * Copyright (c) 2014, 2019 Red Hat Inc. and others.
- * 
+ * Copyright (c) 2014, 2020 Red Hat Inc. and others.
+ *
  * This program and the accompanying materials are made
  * available under the terms of the Eclipse Public License 2.0
  * which is available at https://www.eclipse.org/legal/epl-2.0/
@@ -23,6 +23,7 @@
 import static org.eclipse.linuxtools.internal.docker.ui.launch.IRunDockerImageLaunchConfigurationConstants.PUBLISHED_PORTS;
 import static org.eclipse.linuxtools.internal.docker.ui.launch.IRunDockerImageLaunchConfigurationConstants.PUBLISH_ALL_PORTS;
 import static org.eclipse.linuxtools.internal.docker.ui.launch.IRunDockerImageLaunchConfigurationConstants.READONLY;
+import static org.eclipse.linuxtools.internal.docker.ui.launch.IRunDockerImageLaunchConfigurationConstants.UNUSED_PORTS;
 
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
@@ -103,7 +104,7 @@
 /**
  * A {@link WizardPage} to let the user select the {@link IDockerImage} to run
  * and select the most common arguments (container name, port settings, etc.)
- * 
+ *
  * @author xcoulon
  *
  */
@@ -125,13 +126,13 @@
 
 	/**
 	 * Default constructor.
-	 * 
+	 *
 	 * @param selectedImage
 	 *            the {@link IDockerImage} to run
 	 * @param lastLaunchConfiguration
 	 *            the last {@link ILaunchConfiguration} used to run this
 	 *            {@link IDockerImage} or <code>null</code> if none exists.
-	 * 
+	 *
 	 */
 	public ImageRunSelectionPage(final IDockerImage selectedImage,
 			final ILaunchConfiguration lastLaunchConfiguration) {
@@ -146,10 +147,10 @@
 
 	/**
 	 * Default constructor.
-	 * 
+	 *
 	 * @param selectedConnection
 	 *            the {@link IDockerConnection} to run
-	 * 
+	 *
 	 */
 	public ImageRunSelectionPage(final IDockerConnection selectedConnection) {
 		super("ImageSelectionPage", //$NON-NLS-1$
@@ -269,7 +270,7 @@
 	/**
 	 * Creates the {@link Composite} container that will display widgets to
 	 * select an {@link IDockerImage}, name it and specify the command to run.
-	 * 
+	 *
 	 * @param container
 	 *            the parent {@link Composite}
 	 */
@@ -466,7 +467,6 @@
 						exposedPortsTableViewer, ExposedPortModel.class),
 				BeanProperties.set(ImageRunSelectionModel.SELECTED_PORTS)
 						.observe(model));
-		checkAllElements(exposedPortsTableViewer);
 
 		// disable the edit and removeButton if the table is empty
 		exposedPortsTableViewer.addSelectionChangedListener(
@@ -476,13 +476,6 @@
 				removeButton);
 	}
 
-	private void checkAllElements(
-			final CheckboxTableViewer exposedPortsTableViewer) {
-		exposedPortsTableViewer.setAllChecked(true);
-		model.setSelectedPorts(
-				new HashSet<>(model.getExposedPorts()));
-	}
-
 	private ISelectionChangedListener onSelectionChanged(
 			final Button... targetButtons) {
 		return e -> {
@@ -721,7 +714,7 @@
 	/**
 	 * Creates an {@link IContentProposalProvider} to propose
 	 * {@link IDockerImage} names based on the current text.
-	 * 
+	 *
 	 * @param items
 	 * @return
 	 */
@@ -859,17 +852,26 @@
 				final List<String> exposedPortInfos = lastLaunchConfiguration
 						.getAttribute(PUBLISHED_PORTS,
 								Collections.<String> emptyList());
+				final List<String> unusedPortInfos = lastLaunchConfiguration.getAttribute(UNUSED_PORTS,
+						Collections.<String>emptyList());
 				// FIXME: handle the case where ports where added (and selected)
 				// by the user.
 				if (selectedImageInfo != null) {
-					final List<ExposedPortModel> exposedPorts = ExposedPortModel
-							.fromStrings(
-									selectedImageInfo.config().exposedPorts());
-					model.setExposedPorts(exposedPorts);
-					final List<ExposedPortModel> selectedExposedPorts = ExposedPortModel
-							.fromStrings(exposedPortInfos);
-					this.model.setSelectedPorts(
-							new HashSet<>(selectedExposedPorts));
+					if (exposedPortInfos.isEmpty()) {
+						final List<ExposedPortModel> exposedPorts = ExposedPortModel
+								.fromStrings(selectedImageInfo.config().exposedPorts());
+						model.setExposedPorts(exposedPorts);
+						this.model.setSelectedPorts(new HashSet<>(exposedPorts));
+					} else {
+						final List<ExposedPortModel> exposedPorts = ExposedPortModel.fromStrings(exposedPortInfos);
+						model.setExposedPorts(exposedPorts);
+						this.model.setSelectedPorts(new HashSet<>(exposedPorts));
+					}
+					for (String port : unusedPortInfos) {
+						ExposedPortModel portModel = ExposedPortModel.fromString(port);
+						portModel.setSelected(false);
+						model.addExposedPort(portModel);
+					}
 				}
 
 				// links