Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Johnston2017-02-09 23:43:05 +0000
committerJeff Johnston2017-10-22 09:38:51 +0000
commitc96d126b86382356caec9b8ee961e37b84313f6b (patch)
treef48f38e68d303c6d8c7d9f1b30d22f3c8ea3c59e /launch/org.eclipse.cdt.docker.launcher
parent4c1a6e0ccd90da71cb6d2e7cacfc284a07b180b8 (diff)
downloadorg.eclipse.cdt-c96d126b86382356caec9b8ee961e37b84313f6b.tar.gz
org.eclipse.cdt-c96d126b86382356caec9b8ee961e37b84313f6b.tar.xz
org.eclipse.cdt-c96d126b86382356caec9b8ee961e37b84313f6b.zip
Bug 513589 - Add support to build CDT projects in a Docker Container
- add IOptionalBuildObjectPropertiesContainer interface to use for objects that supply optional build properties - add new IOptionalBuildProperties interface that defines optional build properties donated by external plug-ins - add new - change IConfiguration to an IOptionalBuildObjectPropertiesContainer - change IManagedProject to be an IOptionalBuildObjectPropertiesContainer - fix ProcessClosure to ensure that readers are not null before accessing them - fix Container launch delegate to look at project optional build properties for active configuration to fetch connection and image info and use said info to find a matching launch or create a new one - have Container launch delegate use the image name as part of the launch config name - have Container launch short-cut also use the project's optional build properties for the active config to get connection and image information before any defaulting - change AutotoolsNewMarkerGenerator to store the command launcher as an ICommandLauncher - add new CommandLauncherFactory extension to cdt.core that allows plug-ins to specify a CommandLauncherFactory that will return an ICommandLauncher based on the project - add macros for new extension to CCorePlugin - add new CommandLauncherManager class that loads CommandLauncherFactory extensions and is used to give an ICommandLauncher wrapper that will go through the list of CommandLauncherFactory extensions until one returns non-null ICommandLauncher - add code to RemoteCommandLauncher so it will use the CommandLauncherManager to get the local launcher - also change RemoteCommandLauncher to check at execution time whether the command is local and in that case use the local command launcher - add new ICommandLauncherFactory interface - add new ContainerCommandLauncher to launch - add new ContainerCommandLauncherFactory class for returning a ContainerCommandLauncher instance to launch commands in a Docker Container - change MakeBuilder to use CommandLauncherManager to get its ICommandLauncher - change CommandBuilder to use CommandLauncherManager too - ditto for Builder and AbstractBuiltinSpecsDetector and ExternalToolInvoker - change Configuration to load/store optional build properties as well as return the properties to get/set - ditto for MultiConfiguration - change ManagedProject to implement IOptionalBuildOptionProperties interface - ditto for ProjectType - create new OptionalBuildProperties class to store optional build properties for a configuration - bump cdt.docker.launcher to 1.1.0 - use CommandLauncherFactory extension to define ContainerCommandLauncherFactory - add optional ContainerPropertyTab which allows the end-user to optionally choose to build a C/C++ project in a Container and specify the connection/image to use - in LanguageSettingsSerializableSettings class, call the CommandLauncherManager getLanguageSettingEntries method to get the massaged language setting entries based on the current list - in LanguageSettingsProviderSerializer, try and get the pooled entries using the cfg description so that it will have the project and can use the CommandLauncherManager to get entries from image - in ContainerCommandLauncherFactory move cached headers under a HEADERS directory in the plug-in area - create a sub-directory for the connection and a sub-directory for the image based on cleansed names - store the real names of the connection and image to use later in the DockerHeaderPreferencePage - modify LanguageSettingsEntriesTab to force the horizontal scroll bar to appear (this is a bug in SWT SashForm support and the fix here isn't quite correct, but is better) - add new DockerHeaderPreferencePage that allows user to remove cached headers from images - change C/C++ Docker preferences to be titled: Docker Container - fix LanguageSettingsWorkspaceProvider.getSettingEntries method to use the CommandLauncherManager so entries will be transformed to use cached headers - add BaseDatabindingModel class - add DataVolumeModel class to model a volume mount - add ContainerPropertyVolumes model to model volume specification and selected volumes - add properties to ContainerCommandLauncher to represent volumes and selected volumes for a configuration - add ContainerDataVolumeDialog for specifying a volume mount by the end-user - add a null detector for cfgDescription in LanguageSettingsSerializableProvider - fix AutotoolsNewMakeGenerator.getWinOSType to not specify "." for working dir - fix GCCBuiltinSpecsDetectorCygwin to not map paths to Cygwin if the current configuration is enabled for container build - add logic to ContainerCommandLauncher to look for Windows file formats and change them to unix format and map any "." working dir to be /tmp - fix ContainerLauncherConfigurationDelegate similarly - fix AbstractBuiltinSpecsDetector to pass in the current configuration description when getting the CommandLauncher since the current configuration may not be the active configuration - change ContainerPropertyTab to add Elf and GNU Elf binary parsers when build in Container is chosen so that output executables are treated as Binaries by the CDT project - add documentationl for the ContainerPropertyTab in Build Settings and the Data Volume dialog pop-up it brings up - change CommandBuilder to accept a project as an argument to its constructor and to pass this as an argument to the CommandLauncherManager - have StepBuilder pass project when creating a CommandBuilder Change-Id: Ia78488b93056e6ec7ca83a6c87b3a9d2b9424943
Diffstat (limited to 'launch/org.eclipse.cdt.docker.launcher')
-rw-r--r--launch/org.eclipse.cdt.docker.launcher/META-INF/MANIFEST.MF11
-rw-r--r--launch/org.eclipse.cdt.docker.launcher/icons/error_obj.gifbin0 -> 339 bytes
-rw-r--r--launch/org.eclipse.cdt.docker.launcher/icons/file_obj.gifbin0 -> 349 bytes
-rw-r--r--launch/org.eclipse.cdt.docker.launcher/icons/folder_closed.gifbin0 -> 219 bytes
-rw-r--r--launch/org.eclipse.cdt.docker.launcher/icons/warning_obj.gifbin0 -> 337 bytes
-rw-r--r--launch/org.eclipse.cdt.docker.launcher/plugin.properties10
-rw-r--r--launch/org.eclipse.cdt.docker.launcher/plugin.xml27
-rw-r--r--launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/docker/launcher/ContainerCommandLauncherFactory.java265
-rw-r--r--launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/BaseDatabindingModel.java44
-rw-r--r--launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerCommandLauncher.java429
-rw-r--r--launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerDataVolumeDialog.java466
-rw-r--r--launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerLaunchConfigurationDelegate.java67
-rw-r--r--launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerPropertyTab.java1053
-rw-r--r--launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerPropertyVolumesModel.java142
-rw-r--r--launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerTab.java7
-rw-r--r--launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/DataVolumeModel.java347
-rw-r--r--launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/LaunchShortcut.java102
-rw-r--r--launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/Messages.java15
-rw-r--r--launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/SWTImagesFactory.java13
-rw-r--r--launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/messages.properties15
-rw-r--r--launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ui/preferences/DockerHeaderPreferencePage.java356
21 files changed, 3342 insertions, 27 deletions
diff --git a/launch/org.eclipse.cdt.docker.launcher/META-INF/MANIFEST.MF b/launch/org.eclipse.cdt.docker.launcher/META-INF/MANIFEST.MF
index c24f2f49390..8eb9ae3603f 100644
--- a/launch/org.eclipse.cdt.docker.launcher/META-INF/MANIFEST.MF
+++ b/launch/org.eclipse.cdt.docker.launcher/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %Plugin.name
Bundle-SymbolicName: org.eclipse.cdt.docker.launcher;singleton:=true
-Bundle-Version: 1.0.0.qualifier
+Bundle-Version: 1.1.0.qualifier
Bundle-Activator: org.eclipse.cdt.docker.launcher.DockerLaunchUIPlugin
Bundle-Vendor: %Plugin.vendor
Bundle-Localization: plugin
@@ -20,7 +20,14 @@ Require-Bundle: org.eclipse.ui,
org.eclipse.cdt.debug.ui;bundle-version="7.5.0",
org.eclipse.cdt.dsf.gdb;bundle-version="4.6.0",
org.eclipse.cdt.dsf.gdb.ui;bundle-version="2.4.0",
- org.eclipse.core.variables
+ org.eclipse.core.variables,
+ org.eclipse.cdt.managedbuilder.ui,
+ org.eclipse.cdt.managedbuilder.core,
+ org.eclipse.core.databinding.observable,
+ org.eclipse.core.databinding.beans,
+ org.eclipse.jface.databinding,
+ org.eclipse.core.databinding;bundle-version="1.6.0",
+ org.eclipse.core.databinding.property;bundle-version="1.6.0"
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-ActivationPolicy: lazy
Export-Package: org.eclipse.cdt.docker.launcher;x-internal:=true,
diff --git a/launch/org.eclipse.cdt.docker.launcher/icons/error_obj.gif b/launch/org.eclipse.cdt.docker.launcher/icons/error_obj.gif
new file mode 100644
index 00000000000..0bc60689c6d
--- /dev/null
+++ b/launch/org.eclipse.cdt.docker.launcher/icons/error_obj.gif
Binary files differ
diff --git a/launch/org.eclipse.cdt.docker.launcher/icons/file_obj.gif b/launch/org.eclipse.cdt.docker.launcher/icons/file_obj.gif
new file mode 100644
index 00000000000..efa7a380141
--- /dev/null
+++ b/launch/org.eclipse.cdt.docker.launcher/icons/file_obj.gif
Binary files differ
diff --git a/launch/org.eclipse.cdt.docker.launcher/icons/folder_closed.gif b/launch/org.eclipse.cdt.docker.launcher/icons/folder_closed.gif
new file mode 100644
index 00000000000..42e027c9331
--- /dev/null
+++ b/launch/org.eclipse.cdt.docker.launcher/icons/folder_closed.gif
Binary files differ
diff --git a/launch/org.eclipse.cdt.docker.launcher/icons/warning_obj.gif b/launch/org.eclipse.cdt.docker.launcher/icons/warning_obj.gif
new file mode 100644
index 00000000000..1e5f5eb367c
--- /dev/null
+++ b/launch/org.eclipse.cdt.docker.launcher/icons/warning_obj.gif
Binary files differ
diff --git a/launch/org.eclipse.cdt.docker.launcher/plugin.properties b/launch/org.eclipse.cdt.docker.launcher/plugin.properties
index 0669aa80aad..245288bb62e 100644
--- a/launch/org.eclipse.cdt.docker.launcher/plugin.properties
+++ b/launch/org.eclipse.cdt.docker.launcher/plugin.properties
@@ -1,5 +1,5 @@
###############################################################################
-# Copyright (c) 2015 Red Hat Inc. and others.
+# Copyright (c) 2015,2017 Red Hat Inc. and others.
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
@@ -15,4 +15,10 @@ Delegate.desc=This launcher runs C/C++ applications in a specified Docker Contai
must be set-up to supply the C/C++ application what it needs to run.
LaunchConfigurationType.name=C/C++ Container Launcher
Shortcut.label=C/C++ Container Application
-DockerLaunchPreferencePage.name=Docker Container Launch
+DockerLaunchPreferencePage.name=Docker Container
+DockerHeaderPreferencePage.name=Cached Headers
+ContainerCommandLauncherFactory.name=Container Command Launcher Factory
+Container.settings=Container Settings
+ContainerBuild.property.enablement=Container Build Enablement
+ContainerBuild.property.connection=Container Build Connection
+ContainerBuild.property.image=Container Build Image
diff --git a/launch/org.eclipse.cdt.docker.launcher/plugin.xml b/launch/org.eclipse.cdt.docker.launcher/plugin.xml
index a8e0a2cdb82..a9c3f67e105 100644
--- a/launch/org.eclipse.cdt.docker.launcher/plugin.xml
+++ b/launch/org.eclipse.cdt.docker.launcher/plugin.xml
@@ -65,6 +65,12 @@
id="org.eclipse.cdt.docker.launcher.page1"
name="%DockerLaunchPreferencePage.name">
</page>
+ <page
+ category="org.eclipse.cdt.docker.launcher.page1"
+ class="org.eclipse.cdt.internal.docker.launcher.ui.preferences.DockerHeaderPreferencePage"
+ id="org.eclipse.cdt.docker.launcher.page2"
+ name="%DockerHeaderPreferencePage.name">
+ </page>
</extension>
<extension
point="org.eclipse.debug.ui.launchConfigurationTabGroups">
@@ -88,5 +94,24 @@
id="org.eclipse.cdt.docker.launcher.launchConfigurationTypeImage1">
</launchConfigurationTypeImage>
</extension>
-
+ <extension
+ id="CommandLauncherFactories"
+ name="%ContainerCommandLauncherFactory.name"
+ point="org.eclipse.cdt.core.CommandLauncherFactory">
+ <factory
+ id="ContainerCommandLauncherFactory"
+ class="org.eclipse.cdt.docker.launcher.ContainerCommandLauncherFactory">
+ </factory>
+ </extension>
+ <extension
+ point="org.eclipse.cdt.ui.cPropertyTab">
+ <tab
+ class="org.eclipse.cdt.internal.docker.launcher.ContainerPropertyTab"
+ icon="icons/repository-middle.gif"
+ name="%Container.settings"
+ parent="org.eclipse.cdt.managedbuilder.ui.properties.Page_BuildSettings"
+ weight="020">
+ </tab>
+ </extension>
+
</plugin>
diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/docker/launcher/ContainerCommandLauncherFactory.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/docker/launcher/ContainerCommandLauncherFactory.java
new file mode 100644
index 00000000000..271f173beb0
--- /dev/null
+++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/docker/launcher/ContainerCommandLauncherFactory.java
@@ -0,0 +1,265 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Red Hat Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Red Hat Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.docker.launcher;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.cdt.core.ICommandLauncher;
+import org.eclipse.cdt.core.ICommandLauncherFactory;
+import org.eclipse.cdt.core.model.CoreModel;
+import org.eclipse.cdt.core.settings.model.CIncludePathEntry;
+import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
+import org.eclipse.cdt.core.settings.model.ICIncludePathEntry;
+import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry;
+import org.eclipse.cdt.internal.docker.launcher.ContainerCommandLauncher;
+import org.eclipse.cdt.internal.docker.launcher.Messages;
+import org.eclipse.cdt.managedbuilder.buildproperties.IOptionalBuildProperties;
+import org.eclipse.cdt.managedbuilder.core.IConfiguration;
+import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.linuxtools.docker.ui.launch.ContainerLauncher;
+
+public class ContainerCommandLauncherFactory
+ implements ICommandLauncherFactory {
+
+ @Override
+ public ICommandLauncher getCommandLauncher(IProject project) {
+ // check if container build enablement has been checked
+ ICConfigurationDescription cfgd = CoreModel.getDefault()
+ .getProjectDescription(project)
+ .getActiveConfiguration();
+ IConfiguration cfg = ManagedBuildManager
+ .getConfigurationForDescription(cfgd);
+ // TODO: figure out why this occurs
+ if (cfg == null) {
+ return null;
+ }
+ IOptionalBuildProperties props = cfg.getOptionalBuildProperties();
+ if (props != null) {
+ String enablementProperty = props.getProperty(
+ ContainerCommandLauncher.CONTAINER_BUILD_ENABLED);
+ if (enablementProperty != null) {
+ boolean enableContainer = Boolean
+ .parseBoolean(enablementProperty);
+ // enablement has occurred, we can return a
+ // ContainerCommandLauncher
+ if (enableContainer) {
+ return new ContainerCommandLauncher();
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public ICommandLauncher getCommandLauncher(
+ ICConfigurationDescription cfgd) {
+ // check if container build enablement has been checked
+ IConfiguration cfg = ManagedBuildManager
+ .getConfigurationForDescription(cfgd);
+ // TODO: figure out why this occurs
+ if (cfg == null) {
+ return null;
+ }
+ IOptionalBuildProperties props = cfg.getOptionalBuildProperties();
+ if (props != null) {
+ String enablementProperty = props.getProperty(
+ ContainerCommandLauncher.CONTAINER_BUILD_ENABLED);
+ if (enablementProperty != null) {
+ boolean enableContainer = Boolean
+ .parseBoolean(enablementProperty);
+ // enablement has occurred, we can return a
+ // ContainerCommandLauncher
+ if (enableContainer) {
+ return new ContainerCommandLauncher();
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void registerLanguageSettingEntries(IProject project,
+ List<? extends ICLanguageSettingEntry> langEntries) {
+ @SuppressWarnings("unchecked")
+ List<ICLanguageSettingEntry> entries = (List<ICLanguageSettingEntry>) langEntries;
+ ICConfigurationDescription cfgd = CoreModel.getDefault()
+ .getProjectDescription(project).getActiveConfiguration();
+ IConfiguration cfg = ManagedBuildManager
+ .getConfigurationForDescription(cfgd);
+ IOptionalBuildProperties props = cfg.getOptionalBuildProperties();
+ if (props != null) {
+ String enablementProperty = props.getProperty(
+ ContainerCommandLauncher.CONTAINER_BUILD_ENABLED);
+ if (enablementProperty != null) {
+ boolean enableContainer = Boolean
+ .parseBoolean(enablementProperty);
+ if (enableContainer) {
+ String connectionName = props.getProperty(
+ ContainerCommandLauncher.CONNECTION_ID);
+ String imageName = props
+ .getProperty(ContainerCommandLauncher.IMAGE_ID);
+ if (connectionName == null || connectionName.isEmpty()
+ || imageName == null || imageName.isEmpty()) {
+ DockerLaunchUIPlugin.logErrorMessage(
+ Messages.ContainerCommandLauncher_invalid_values);
+ return;
+ }
+ ContainerLauncher launcher = new ContainerLauncher();
+ List<String> paths = new ArrayList<>();
+ for (ICLanguageSettingEntry entry : entries) {
+ if (entry instanceof ICIncludePathEntry) {
+ paths.add(entry.getValue());
+ }
+ }
+ if (paths.size() > 0) {
+ // Create a directory to put the header files for
+ // the image. Use the connection name to form
+ // the directory name as the connection may be
+ // connected to a different repo using the same
+ // image name.
+ IPath pluginPath = Platform.getStateLocation(Platform
+ .getBundle(DockerLaunchUIPlugin.PLUGIN_ID))
+ .append("HEADERS"); //$NON-NLS-1$
+ pluginPath.toFile().mkdir();
+ pluginPath = pluginPath
+ .append(getCleanName(connectionName));
+ pluginPath.toFile().mkdir();
+ // To allow the user to later manage the headers, store
+ // the
+ // real connection name in a file.
+ IPath connectionNamePath = pluginPath.append(".name"); //$NON-NLS-1$
+ File f = connectionNamePath.toFile();
+ try {
+ f.createNewFile();
+ try (FileWriter writer = new FileWriter(f);
+ BufferedWriter bufferedWriter = new BufferedWriter(
+ writer);) {
+ bufferedWriter.write(connectionName);
+ bufferedWriter.newLine();
+ } catch (IOException e) {
+ DockerLaunchUIPlugin.log(e);
+ return;
+ }
+ pluginPath = pluginPath
+ .append(getCleanName(imageName));
+ pluginPath.toFile().mkdir();
+ // To allow the user to later manage the headers,
+ // store the
+ // real image name in a file.
+ IPath imageNamePath = pluginPath.append(".name"); //$NON-NLS-1$
+ f = imageNamePath.toFile();
+ f.createNewFile();
+ try (FileWriter writer = new FileWriter(f);
+ BufferedWriter bufferedWriter = new BufferedWriter(
+ writer);) {
+ bufferedWriter.write(imageName);
+ bufferedWriter.newLine();
+ } catch (IOException e) {
+ DockerLaunchUIPlugin.log(e);
+ return;
+ }
+ } catch (IOException e) {
+ DockerLaunchUIPlugin.log(e);
+ return;
+ }
+ IPath hostDir = pluginPath;
+ @SuppressWarnings("unused")
+ int status = launcher.fetchContainerDirs(connectionName,
+ imageName,
+ paths, hostDir);
+ }
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public List<ICLanguageSettingEntry> verifyLanguageSettingEntries(
+ IProject project, List<ICLanguageSettingEntry> entries) {
+ if (entries == null) {
+ return null;
+ }
+ ICConfigurationDescription cfgd = CoreModel.getDefault()
+ .getProjectDescription(project).getActiveConfiguration();
+ IConfiguration cfg = ManagedBuildManager
+ .getConfigurationForDescription(cfgd);
+ IOptionalBuildProperties props = cfg.getOptionalBuildProperties();
+ if (props != null) {
+ String enablementProperty = props.getProperty(
+ ContainerCommandLauncher.CONTAINER_BUILD_ENABLED);
+ if (enablementProperty != null) {
+ boolean enableContainer = Boolean
+ .parseBoolean(enablementProperty);
+ if (enableContainer) {
+ String connectionName = props.getProperty(
+ ContainerCommandLauncher.CONNECTION_ID);
+ String imageName = props
+ .getProperty(ContainerCommandLauncher.IMAGE_ID);
+ if (connectionName == null || connectionName.isEmpty()
+ || imageName == null || imageName.isEmpty()) {
+ DockerLaunchUIPlugin.logErrorMessage(
+ Messages.ContainerCommandLauncher_invalid_values);
+ return entries;
+ }
+
+ ContainerLauncher launcher = new ContainerLauncher();
+ Set<String> copiedVolumes = launcher
+ .getCopiedVolumes(connectionName, imageName);
+ List<ICLanguageSettingEntry> newEntries = new ArrayList<>();
+ IPath pluginPath = Platform.getStateLocation(
+ Platform.getBundle(DockerLaunchUIPlugin.PLUGIN_ID));
+ IPath hostDir = pluginPath.append("HEADERS") //$NON-NLS-1$
+ .append(getCleanName(connectionName))
+ .append(getCleanName(imageName));
+
+ for (ICLanguageSettingEntry entry : entries) {
+ if (entry instanceof ICIncludePathEntry) {
+ if (copiedVolumes
+ .contains(((ICIncludePathEntry) entry)
+ .getName().toString())) {
+ // //$NON-NLS-2$
+ IPath newPath = hostDir.append(entry.getName());
+ CIncludePathEntry newEntry = new CIncludePathEntry(
+ newPath.toString(),
+ entry.getFlags());
+ newEntries.add(newEntry);
+ continue;
+ } else {
+ newEntries.add(entry);
+ }
+ } else {
+ newEntries.add(entry);
+ }
+ }
+ return newEntries;
+ }
+ }
+ }
+ return entries;
+ }
+
+ private String getCleanName(String name) {
+ String cleanName = name.replace("unix:///", "unix_"); //$NON-NLS-1$ //$NON-NLS-2$
+ cleanName = cleanName.replace("tcp://", "tcp_"); //$NON-NLS-1$ //$NON-NLS-2$
+ return cleanName.replaceAll("[:/.]", "_"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+}
diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/BaseDatabindingModel.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/BaseDatabindingModel.java
new file mode 100644
index 00000000000..b17d89701ac
--- /dev/null
+++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/BaseDatabindingModel.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 2015 Red Hat.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Red Hat - Initial Contribution
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.docker.launcher;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+
+/**
+ * Base class for all model classes that need Databinding support
+ *
+ */
+public abstract class BaseDatabindingModel {
+
+ private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
+
+ public void addPropertyChangeListener(PropertyChangeListener listener) {
+ changeSupport.addPropertyChangeListener(listener);
+ }
+
+ public void removePropertyChangeListener(PropertyChangeListener listener) {
+ changeSupport.removePropertyChangeListener(listener);
+ }
+
+ public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
+ changeSupport.addPropertyChangeListener(propertyName, listener);
+ }
+
+ public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
+ changeSupport.removePropertyChangeListener(propertyName, listener);
+ }
+
+ protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
+ changeSupport.firePropertyChange(propertyName, oldValue, newValue);
+ }
+}
diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerCommandLauncher.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerCommandLauncher.java
new file mode 100644
index 00000000000..79db65bd631
--- /dev/null
+++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerCommandLauncher.java
@@ -0,0 +1,429 @@
+package org.eclipse.cdt.internal.docker.launcher;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.eclipse.cdt.core.ICommandLauncher;
+import org.eclipse.cdt.core.model.CoreModel;
+import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
+import org.eclipse.cdt.docker.launcher.DockerLaunchUIPlugin;
+import org.eclipse.cdt.internal.core.ProcessClosure;
+import org.eclipse.cdt.managedbuilder.buildproperties.IOptionalBuildProperties;
+import org.eclipse.cdt.managedbuilder.core.IConfiguration;
+import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.core.variables.VariablesPlugin;
+import org.eclipse.linuxtools.docker.ui.launch.ContainerLauncher;
+import org.eclipse.linuxtools.docker.ui.launch.IErrorMessageHolder;
+import org.eclipse.linuxtools.internal.docker.ui.launch.ContainerCommandProcess;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.service.prefs.Preferences;
+
+@SuppressWarnings("restriction")
+public class ContainerCommandLauncher
+ implements ICommandLauncher, IErrorMessageHolder {
+
+ public final static String CONTAINER_BUILD_ENABLED = DockerLaunchUIPlugin.PLUGIN_ID
+ + ".containerbuild.property.enablement"; //$NON-NLS-1$
+ public final static String CONNECTION_ID = DockerLaunchUIPlugin.PLUGIN_ID
+ + ".containerbuild.property.connection"; //$NON-NLS-1$
+ public final static String IMAGE_ID = DockerLaunchUIPlugin.PLUGIN_ID
+ + ".containerbuild.property.image"; //$NON-NLS-1$
+ public final static String VOLUMES_ID = DockerLaunchUIPlugin.PLUGIN_ID
+ + ".containerbuild.property.volumes"; //$NON-NLS-1$
+ public final static String SELECTED_VOLUMES_ID = DockerLaunchUIPlugin.PLUGIN_ID
+ + ".containerbuild.property.selectedvolumes"; //$NON-NLS-1$
+
+ public final static String VOLUME_SEPARATOR_REGEX = "[|]"; //$NON-NLS-1$
+
+ private IProject fProject;
+ private Process fProcess;
+ private boolean fShowCommand;
+ private String fErrorMessage;
+ private Properties fEnvironment;
+
+ private String[] commandArgs;
+ private String fImageName = ""; //$NON-NLS-1$
+
+ public final static int COMMAND_CANCELED = ICommandLauncher.COMMAND_CANCELED;
+ public final static int ILLEGAL_COMMAND = ICommandLauncher.ILLEGAL_COMMAND;
+ public final static int OK = ICommandLauncher.OK;
+
+ private static final String NEWLINE = System.getProperty("line.separator", //$NON-NLS-1$
+ "\n"); //$NON-NLS-1$
+
+ /**
+ * The number of milliseconds to pause between polling.
+ */
+ protected static final long DELAY = 50L;
+
+ @Override
+ public void setProject(IProject project) {
+ this.fProject = project;
+ }
+
+ @Override
+ public IProject getProject() {
+ return fProject;
+ }
+
+ private String getImageName() {
+ return fImageName;
+ }
+
+ private void setImageName(String imageName) {
+ fImageName = imageName;
+ }
+
+ @Override
+ public void showCommand(boolean show) {
+ this.fShowCommand = show;
+ }
+
+ @Override
+ public String getErrorMessage() {
+ return fErrorMessage;
+ }
+
+ @Override
+ public void setErrorMessage(String error) {
+ fErrorMessage = error;
+ }
+
+ @Override
+ public String[] getCommandArgs() {
+ return commandArgs;
+ }
+
+ @Override
+ public Properties getEnvironment() {
+ return fEnvironment;
+ }
+
+ @Override
+ public String getCommandLine() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Process execute(IPath commandPath, String[] args, String[] env,
+ IPath workingDirectory, IProgressMonitor monitor)
+ throws CoreException {
+
+ HashMap<String, String> labels = new HashMap<>();
+ labels.put("org.eclipse.cdt.container-command", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ String projectName = fProject.getName();
+ labels.put("org.eclipse.cdt.project-name", projectName); //$NON-NLS-1$
+
+ List<String> additionalDirs = new ArrayList<>();
+
+ //
+ IPath projectLocation = fProject.getLocation();
+ String projectPath = projectLocation.toPortableString();
+ if (projectLocation.getDevice() != null) {
+ projectPath = "/" + projectPath.replace(':', '/'); //$NON-NLS-1$
+ }
+ additionalDirs.add(projectPath);
+
+ ArrayList<String> commandSegments = new ArrayList<>();
+
+ StringBuilder b = new StringBuilder();
+ String commandString = commandPath.toPortableString();
+ if (commandPath.getDevice() != null) {
+ commandString = "/" + commandString.replace(':', '/'); //$NON-NLS-1$
+ }
+ b.append(commandString);
+ commandSegments.add(commandString);
+ for (String arg : args) {
+ b.append(" "); //$NON-NLS-1$
+ String realArg = VariablesPlugin.getDefault()
+ .getStringVariableManager().performStringSubstitution(arg);
+ if (Platform.getOS().equals(Platform.OS_WIN32)) {
+ // check if file exists and if so, add an additional directory
+ IPath p = new Path(realArg);
+ if (p.isValidPath(realArg) && p.getDevice() != null) {
+ File f = p.toFile();
+ String modifiedArg = realArg;
+ // if the directory of the arg as a file exists, we mount it
+ // and modify the argument to be unix-style
+ if (f.isFile()) {
+ f = f.getParentFile();
+ modifiedArg = "/" //$NON-NLS-1$
+ + p.toPortableString().replace(':', '/');
+ p = p.removeLastSegments(1);
+ }
+ if (f != null && f.exists()) {
+ additionalDirs.add(
+ "/" + p.toPortableString().replace(':', '/')); //$NON-NLS-1$
+ realArg = modifiedArg;
+ }
+ }
+ } else if (realArg.startsWith("/")) { //$NON-NLS-1$
+ // check if file directory exists and if so, add an additional
+ // directory
+ IPath p = new Path(realArg);
+ if (p.isValidPath(realArg)) {
+ File f = p.toFile();
+ if (f.isFile()) {
+ f = f.getParentFile();
+ }
+ if (f != null && f.exists()) {
+ additionalDirs.add(f.getAbsolutePath());
+ }
+ }
+ }
+ b.append(realArg);
+ commandSegments.add(realArg);
+ }
+
+ commandArgs = commandSegments.toArray(new String[0]);
+
+ String commandDir = commandPath.removeLastSegments(1).toString();
+ if (commandDir.isEmpty()) {
+ commandDir = null;
+ } else if (commandPath.getDevice() != null) {
+ commandDir = "/" + commandDir.replace(':', '/'); //$NON-NLS-1$
+ }
+
+ IProject[] referencedProjects = fProject.getReferencedProjects();
+ for (IProject referencedProject : referencedProjects) {
+ String referencedProjectPath = referencedProject.getLocation()
+ .toPortableString();
+ if (referencedProject.getLocation().getDevice() != null) {
+ referencedProjectPath = "/" //$NON-NLS-1$
+ + referencedProjectPath.replace(':', '/');
+ }
+ additionalDirs
+ .add(referencedProjectPath);
+ }
+
+ String command = b.toString();
+
+ String workingDir = workingDirectory.makeAbsolute().toPortableString();
+ if (workingDirectory.toPortableString().equals(".")) { //$NON-NLS-1$
+ workingDir = "/tmp"; //$NON-NLS-1$
+ } else if (workingDirectory.getDevice() != null) {
+ workingDir = "/" + workingDir.replace(':', '/'); //$NON-NLS-1$
+ }
+ parseEnvironment(env);
+ Map<String, String> origEnv = null;
+
+ boolean supportStdin = false;
+
+ boolean privilegedMode = false;
+
+ ContainerLauncher launcher = new ContainerLauncher();
+
+ Preferences prefs = InstanceScope.INSTANCE
+ .getNode(DockerLaunchUIPlugin.PLUGIN_ID);
+
+ boolean keepContainer = prefs.getBoolean(
+ PreferenceConstants.KEEP_CONTAINER_AFTER_LAUNCH, false);
+
+ ICConfigurationDescription cfgd = CoreModel.getDefault()
+ .getProjectDescription(fProject).getActiveConfiguration();
+ IConfiguration cfg = ManagedBuildManager
+ .getConfigurationForDescription(cfgd);
+ if (cfg == null) {
+ return null;
+ }
+ IOptionalBuildProperties props = cfg.getOptionalBuildProperties();
+
+ // Add any specified volumes to additional dir list
+ String selectedVolumeString = props.getProperty(SELECTED_VOLUMES_ID);
+ if (selectedVolumeString != null && !selectedVolumeString.isEmpty()) {
+ String[] selectedVolumes = selectedVolumeString
+ .split(VOLUME_SEPARATOR_REGEX);
+ if (Platform.getOS().equals(Platform.OS_WIN32)) {
+ for (String selectedVolume : selectedVolumes) {
+ IPath path = new Path(selectedVolume);
+ String selectedPath = path.toPortableString();
+ if (path.getDevice() != null) {
+ selectedPath = "/" + selectedPath.replace(':', '/'); //$NON-NLS-1$
+ }
+ additionalDirs.add(selectedPath);
+ }
+ } else {
+ additionalDirs.addAll(Arrays.asList(selectedVolumes));
+ }
+ }
+
+ String connectionName = props
+ .getProperty(ContainerCommandLauncher.CONNECTION_ID);
+ if (connectionName == null) {
+ return null;
+ }
+ String imageName = props
+ .getProperty(ContainerCommandLauncher.IMAGE_ID);
+ if (imageName == null) {
+ return null;
+ }
+ setImageName(imageName);
+
+ fProcess = launcher.runCommand(connectionName, imageName, fProject,
+ this,
+ command,
+ commandDir,
+ workingDir,
+ additionalDirs,
+ origEnv, fEnvironment, supportStdin, privilegedMode,
+ labels, keepContainer);
+
+ return fProcess;
+ }
+
+ /**
+ * Parse array of "ENV=value" pairs to Properties.
+ */
+ private void parseEnvironment(String[] env) {
+ fEnvironment = null;
+ if (env != null) {
+ fEnvironment = new Properties();
+ for (String envStr : env) {
+ // Split "ENV=value" and put in Properties
+ int pos = envStr.indexOf('='); // $NON-NLS-1$
+ if (pos < 0)
+ pos = envStr.length();
+ String key = envStr.substring(0, pos);
+ String value = envStr.substring(pos + 1);
+ fEnvironment.put(key, value);
+ }
+ }
+ }
+
+ @Override
+ public int waitAndRead(OutputStream out, OutputStream err) {
+ printImageHeader(out);
+
+ if (fShowCommand) {
+ printCommandLine(out);
+ }
+
+ if (fProcess == null) {
+ return ILLEGAL_COMMAND;
+ }
+ ProcessClosure closure = new ProcessClosure(fProcess, out, err);
+ closure.runBlocking(); // a blocking call
+ return OK;
+ }
+
+ @Override
+ public int waitAndRead(OutputStream output, OutputStream err,
+ IProgressMonitor monitor) {
+ printImageHeader(output);
+
+ if (fShowCommand) {
+ printCommandLine(output);
+ }
+
+ if (fProcess == null) {
+ return ILLEGAL_COMMAND;
+ }
+
+ ProcessClosure closure = new ProcessClosure(fProcess, output, err);
+ closure.runNonBlocking();
+ Runnable watchProcess = () -> {
+ try {
+ fProcess.waitFor();
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ closure.terminate();
+ };
+ Thread t = new Thread(watchProcess);
+ t.start();
+ while (!monitor.isCanceled() && closure.isAlive()) {
+ try {
+ Thread.sleep(DELAY);
+ } catch (InterruptedException ie) {
+ break;
+ }
+ }
+ try {
+ t.join(500);
+ } catch (InterruptedException e1) {
+ // ignore
+ }
+ int state = OK;
+
+ // Operation canceled by the user, terminate abnormally.
+ if (monitor.isCanceled()) {
+ closure.terminate();
+ state = COMMAND_CANCELED;
+ setErrorMessage(Messages.CommandLauncher_CommandCancelled);
+ }
+ try {
+ fProcess.waitFor();
+ } catch (InterruptedException e) {
+ // ignore
+ }
+
+ monitor.done();
+ return state;
+ }
+
+ protected void printImageHeader(OutputStream os) {
+ if (os != null) {
+ try {
+ os.write(NLS
+ .bind(Messages.ContainerCommandLauncher_image_msg,
+ ((ContainerCommandProcess) fProcess).getImage())
+ .getBytes());
+ os.write(NEWLINE.getBytes());
+ os.flush();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+
+ protected void printCommandLine(OutputStream os) {
+ if (os != null) {
+ try {
+ os.write(getCommandLineQuoted(getCommandArgs(), true)
+ .getBytes());
+ os.flush();
+ } catch (IOException e) {
+ // ignore;
+ }
+ }
+ }
+
+ @SuppressWarnings("nls")
+ private String getCommandLineQuoted(String[] commandArgs, boolean quote) {
+ StringBuilder buf = new StringBuilder();
+ if (commandArgs != null) {
+ for (String commandArg : commandArgs) {
+ if (quote && (commandArg.contains(" ")
+ || commandArg.contains("\"")
+ || commandArg.contains("\\"))) {
+ commandArg = '"' + commandArg.replaceAll("\\\\", "\\\\\\\\")
+ .replaceAll("\"", "\\\\\"") + '"';
+ }
+ buf.append(commandArg);
+ buf.append(' ');
+ }
+ buf.append(NEWLINE);
+ }
+ return buf.toString();
+ }
+
+ protected String getCommandLine(String[] commandArgs) {
+ return getCommandLineQuoted(commandArgs, false);
+ }
+
+}
diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerDataVolumeDialog.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerDataVolumeDialog.java
new file mode 100644
index 00000000000..8e8af909e81
--- /dev/null
+++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerDataVolumeDialog.java
@@ -0,0 +1,466 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 Red Hat Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Red Hat - Initial Contribution
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.docker.launcher;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.cdt.internal.docker.launcher.ContainerPropertyVolumesModel.MountType;
+import org.eclipse.core.databinding.Binding;
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.UpdateValueStrategy;
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.observable.IChangeListener;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.databinding.swt.ISWTObservableValue;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.fieldassist.ComboContentAdapter;
+import org.eclipse.jface.fieldassist.ContentProposal;
+import org.eclipse.jface.fieldassist.ContentProposalAdapter;
+import org.eclipse.jface.fieldassist.ControlDecoration;
+import org.eclipse.jface.fieldassist.IContentProposal;
+import org.eclipse.jface.fieldassist.IContentProposalProvider;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.linuxtools.docker.core.IDockerConnection;
+import org.eclipse.linuxtools.docker.core.IDockerContainer;
+import org.eclipse.linuxtools.docker.core.IDockerContainerInfo;
+import org.eclipse.linuxtools.internal.docker.ui.wizards.WizardMessages;
+import org.eclipse.linuxtools.internal.docker.ui.wizards.WizardUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * @author xcoulon
+ *
+ */
+public class ContainerDataVolumeDialog extends Dialog {
+
+ private final DataVolumeModel model;
+
+ private final DataBindingContext dbc = new DataBindingContext();
+
+ private final List<String> containerNames;
+
+ private final IDockerConnection connection;
+
+ public ContainerDataVolumeDialog(final Shell parentShell,
+ final IDockerConnection connection,
+ final DataVolumeModel selectedDataVolume) {
+ super(parentShell);
+ this.connection = connection;
+ this.model = new DataVolumeModel(selectedDataVolume);
+ this.containerNames = WizardUtils.getContainerNames(connection);
+ }
+
+ public ContainerDataVolumeDialog(final Shell parentShell,
+ final IDockerConnection connection) {
+ super(parentShell);
+ this.connection = connection;
+ this.model = new DataVolumeModel();
+ this.containerNames = WizardUtils.getContainerNames(connection);
+ }
+
+ public DataVolumeModel getDataVolume() {
+ return model;
+ }
+
+ @Override
+ protected void configureShell(final Shell shell) {
+ super.configureShell(shell);
+ setShellStyle(getShellStyle() | SWT.RESIZE);
+ shell.setText(
+ WizardMessages.getString("ContainerDataVolumeDialog.title")); //$NON-NLS-1$
+ }
+
+ /**
+ * Disable the 'OK' button by default
+ */
+ @Override
+ protected Button createButton(Composite parent, int id, String label,
+ boolean defaultButton) {
+ final Button button = super.createButton(parent, id, label,
+ defaultButton);
+ if (id == IDialogConstants.OK_ID) {
+ button.setEnabled(false);
+ }
+ return button;
+ }
+
+ @Override
+ protected Point getInitialSize() {
+ return new Point(450, super.getInitialSize().y);
+ }
+
+ @Override
+ protected Control createDialogArea(final Composite parent) {
+ final Composite container = new Composite(parent, SWT.NONE);
+ final int COLUMNS = 3;
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).span(1, 1)
+ .grab(true, true).applyTo(container);
+ GridLayoutFactory.fillDefaults().margins(10, 10).numColumns(COLUMNS)
+ .applyTo(container);
+
+ // Container path
+ final Label containerPathLabel = new Label(container, SWT.NONE);
+ containerPathLabel.setText(WizardMessages
+ .getString("ContainerDataVolumeDialog.containerPathLabel")); //$NON-NLS-1$
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
+ .grab(false, false).applyTo(containerPathLabel);
+ final Text containerPathText = new Text(container, SWT.BORDER);
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
+ .grab(true, false).applyTo(containerPathText);
+ final IObservableValue containerPathObservable = BeanProperties
+ .value(DataVolumeModel.class, DataVolumeModel.CONTAINER_PATH)
+ .observe(model);
+ dbc.bindValue(
+ WidgetProperties.text(SWT.Modify).observe(containerPathText),
+ containerPathObservable);
+ // mount type
+ final Label explanationLabel = new Label(container, SWT.NONE);
+ explanationLabel.setText(WizardMessages
+ .getString("ContainerDataVolumeDialog.explanationLabel")); //$NON-NLS-1$
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
+ .span(COLUMNS, 1).grab(true, false).applyTo(explanationLabel);
+ final int INDENT = 20;
+ // No mount
+ final Button noMountButton = new Button(container, SWT.RADIO);
+ noMountButton.setText(WizardMessages
+ .getString("ContainerDataVolumeDialog.noMountButton")); //$NON-NLS-1$
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
+ .indent(INDENT, 0).span(COLUMNS, 1).grab(true, false)
+ .applyTo(noMountButton);
+ bindButton(noMountButton, MountType.NONE);
+ // File System mount
+ final Button fileSystemMountButton = new Button(container, SWT.RADIO);
+ fileSystemMountButton.setText(WizardMessages
+ .getString("ContainerDataVolumeDialog.fileSystemMountButton")); //$NON-NLS-1$
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
+ .indent(INDENT, 0).span(COLUMNS, 1).grab(true, false)
+ .applyTo(fileSystemMountButton);
+ final Label hostPathLabel = new Label(container, SWT.NONE);
+ hostPathLabel.setText(WizardMessages
+ .getString("ContainerDataVolumeDialog.hostPathLabel")); //$NON-NLS-1$
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
+ .indent(2 * INDENT, SWT.DEFAULT).grab(false, false)
+ .applyTo(hostPathLabel);
+ final Text hostPathText = new Text(container, SWT.BORDER);
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
+ .grab(true, false).applyTo(hostPathText);
+ final IObservableValue hostPathObservable = BeanProperties
+ .value(DataVolumeModel.class, DataVolumeModel.HOST_PATH_MOUNT)
+ .observe(model);
+ dbc.bindValue(WidgetProperties.text(SWT.Modify).observe(hostPathText),
+ hostPathObservable);
+ // browse for directory
+ final Button hostPathDirectoryButton = new Button(container, SWT.NONE);
+ hostPathDirectoryButton.setText(WizardMessages
+ .getString("ContainerDataVolumeDialog.directoryButton")); //$NON-NLS-1$
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
+ .grab(false, false).applyTo(hostPathDirectoryButton);
+ hostPathDirectoryButton.addSelectionListener(onHostDirectoryPath());
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
+ .grab(false, false).applyTo(new Label(container, SWT.NONE));
+ // optional read-only access
+ final Button readOnlyButton = new Button(container, SWT.CHECK);
+ readOnlyButton.setText(WizardMessages
+ .getString("ContainerDataVolumeDialog.readOnlyButton")); //$NON-NLS-1$
+ readOnlyButton.setToolTipText(WizardMessages
+ .getString("ContainerDataVolumeDialog.readOnlyButtonTooltip")); //$NON-NLS-1$
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
+ .span(COLUMNS - 2, 1).grab(true, false).applyTo(readOnlyButton);
+ final ISWTObservableValue readOnlyButtonObservable = WidgetProperties
+ .selection().observe(readOnlyButton);
+ dbc.bindValue(readOnlyButtonObservable,
+ BeanProperties
+ .value(DataVolumeModel.class,
+ DataVolumeModel.READ_ONLY_VOLUME)
+ .observe(model));
+ // browse for file
+ final Button hostPathFileButton = new Button(container, SWT.NONE);
+ hostPathFileButton.setText(WizardMessages
+ .getString("ContainerDataVolumeDialog.fileButton")); //$NON-NLS-1$
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
+ .grab(false, false).applyTo(hostPathFileButton);
+ hostPathFileButton.addSelectionListener(onHostFilePath());
+ bindButton(fileSystemMountButton, MountType.HOST_FILE_SYSTEM,
+ hostPathText, hostPathDirectoryButton, hostPathFileButton,
+ readOnlyButton);
+
+ // Container mount
+ final Button containerMountButton = new Button(container, SWT.RADIO);
+ containerMountButton.setText(WizardMessages
+ .getString("ContainerDataVolumeDialog.containerMountButton")); //$NON-NLS-1$
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
+ .indent(INDENT, 0).span(COLUMNS, 1).grab(true, false)
+ .applyTo(containerMountButton);
+ final Label containerSelectionLabel = new Label(container, SWT.NONE);
+ containerSelectionLabel.setText(WizardMessages.getString(
+ "ContainerDataVolumeDialog.containerSelectionLabel")); //$NON-NLS-1$
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
+ .indent(2 * INDENT, SWT.DEFAULT)
+ .applyTo(containerSelectionLabel);
+ final Combo containerSelectionCombo = new Combo(container, SWT.BORDER);
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
+ .grab(true, false).span(1, 1).applyTo(containerSelectionCombo);
+ new ControlDecoration(containerSelectionCombo, SWT.TOP | SWT.LEFT);
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
+ .grab(false, false).applyTo(new Label(container, SWT.NONE));
+ bindButton(containerMountButton, MountType.CONTAINER,
+ containerSelectionCombo);
+ final ComboViewer containerSelectionComboViewer = new ComboViewer(
+ containerSelectionCombo);
+ containerSelectionComboViewer
+ .setContentProvider(new ArrayContentProvider());
+ containerSelectionComboViewer.setInput(this.containerNames);
+ final IObservableValue selectedContainerObservable = BeanProperties
+ .value(DataVolumeModel.class, DataVolumeModel.CONTAINER_MOUNT)
+ .observe(model);
+ dbc.bindValue(
+ WidgetProperties.selection().observe(containerSelectionCombo),
+ selectedContainerObservable);
+ new ContentProposalAdapter(containerSelectionCombo,
+ new ComboContentAdapter() {
+ @Override
+ public void insertControlContents(Control control,
+ String text, int cursorPosition) {
+ final Combo combo = (Combo) control;
+ final Point selection = combo.getSelection();
+ combo.setText(text);
+ selection.x = text.length();
+ selection.y = selection.x;
+ combo.setSelection(selection);
+ }
+ }, getContainerNameContentProposalProvider(
+ containerSelectionCombo),
+ null, null);
+
+ // error message
+ final Composite errorContainer = new Composite(container, SWT.NONE);
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL)
+ .span(COLUMNS, 1).grab(true, true).applyTo(errorContainer);
+ GridLayoutFactory.fillDefaults().margins(6, 6).numColumns(2)
+ .applyTo(errorContainer);
+
+ final Label errorMessageIcon = new Label(errorContainer, SWT.NONE);
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
+ .hint(20, SWT.DEFAULT)
+ .applyTo(errorMessageIcon);
+ final Label errorMessageLabel = new Label(errorContainer, SWT.NONE);
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
+ .grab(true, false)
+ .applyTo(errorMessageLabel);
+ setupValidationSupport(errorMessageIcon, errorMessageLabel);
+ return container;
+ }
+
+ private void setupValidationSupport(final Label errorMessageIcon,
+ final Label errorMessageLabel) {
+ for (@SuppressWarnings("unchecked")
+ Iterator<Binding> iterator = dbc.getBindings().iterator(); iterator
+ .hasNext();) {
+ final Binding binding = iterator.next();
+ binding.getModel().addChangeListener(onDataVolumeSettingsChanged(
+ errorMessageIcon, errorMessageLabel));
+ }
+ }
+
+ /**
+ * Binds the given {@link MountType} to the given {@link Button} when it is
+ * selected, and set the enablement of the associated {@link Control} at the
+ * same time (ie: the {@link Control} are only enabled when the given
+ * {@link Button} is selected.
+ *
+ * @param button
+ * the {@link Button} to bind
+ * @param mountType
+ * the {@link MountType} to bind to the {@link Button}
+ * @param controls
+ * the {@link Control}s to enable or disable when the Button is
+ * selected/unselected.
+ * @return
+ */
+ private Binding bindButton(final Button button, final MountType mountType,
+ final Control... controls) {
+ return dbc.bindValue(WidgetProperties.selection().observe(button),
+ BeanProperties.value(DataVolumeModel.class,
+ DataVolumeModel.MOUNT_TYPE).observe(model),
+ new UpdateValueStrategy() {
+ @Override
+ public Object convert(Object value) {
+ if (value.equals(Boolean.TRUE)) {
+ setEnabled(controls, true);
+ return mountType;
+ }
+ setEnabled(controls, false);
+ return null;
+ }
+
+ private void setEnabled(final Control[] controls,
+ final boolean enabled) {
+ for (Control control : controls) {
+ control.setEnabled(enabled);
+ }
+ }
+ }, new UpdateValueStrategy() {
+ @Override
+ public Object convert(final Object value) {
+ if (mountType.equals(value)) {
+ button.setEnabled(true);
+ }
+ return mountType.equals(value);
+ }
+ });
+ }
+
+ private SelectionListener onHostDirectoryPath() {
+ return SelectionListener.widgetSelectedAdapter(e -> {
+ final DirectoryDialog directoryDialog = new DirectoryDialog(
+ getShell());
+ final String selectedPath = directoryDialog.open();
+ if (selectedPath != null) {
+ model.setHostPathMount(selectedPath);
+ }
+ });
+ }
+
+ private SelectionListener onHostFilePath() {
+ return SelectionListener.widgetSelectedAdapter(e -> {
+ final FileDialog fileDialog = new FileDialog(getShell());
+ final String selectedPath = fileDialog.open();
+ if (selectedPath != null) {
+ model.setHostPathMount(selectedPath);
+ }
+ });
+ }
+
+ /**
+ * Creates an {@link IContentProposalProvider} to propose
+ * {@link IDockerContainer} names based on the current text.
+ *
+ * @param items
+ * @return
+ */
+ private IContentProposalProvider getContainerNameContentProposalProvider(
+ final Combo containerSelectionCombo) {
+ return (contents, position) -> {
+ final List<IContentProposal> proposals = new ArrayList<>();
+ for (String containerName : containerSelectionCombo.getItems()) {
+ if (containerName.contains(contents)) {
+ proposals.add(new ContentProposal(containerName,
+ containerName, containerName, position));
+ }
+ }
+ return proposals.toArray(new IContentProposal[0]);
+ };
+ }
+
+ private IChangeListener onDataVolumeSettingsChanged(
+ final Label errorMessageIcon, final Label errorMessageLabel) {
+
+ return event -> {
+ // skip if dialog has been closed
+ if (Display.getCurrent() == null || getShell().isDisposed()) {
+ return;
+ }
+ final IStatus status = validateInput();
+ Display.getCurrent().syncExec(() -> {
+ if (status.isOK()) {
+ errorMessageIcon.setVisible(false);
+ errorMessageLabel.setVisible(false);
+ setOkButtonEnabled(true);
+ } else if (status.matches(IStatus.WARNING)) {
+ errorMessageIcon.setVisible(true);
+ errorMessageIcon.setImage(
+ SWTImagesFactory.DESC_WARNING.createImage());
+ errorMessageLabel.setVisible(true);
+ errorMessageLabel.setText(status.getMessage());
+ setOkButtonEnabled(true);
+ } else if (status.matches(IStatus.ERROR)) {
+ if (status.getMessage() != null
+ && !status.getMessage().isEmpty()) {
+ errorMessageIcon.setVisible(true);
+ errorMessageIcon.setImage(
+ SWTImagesFactory.DESC_ERROR.createImage());
+ errorMessageLabel.setVisible(true);
+ errorMessageLabel.setText(status.getMessage());
+ }
+ setOkButtonEnabled(false);
+ }
+ });
+ };
+ }
+
+ private IStatus validateInput() {
+ final String containerPath = model.getContainerPath();
+ final MountType mountType = model.getMountType();
+ final String hostPath = model.getHostPathMount();
+ if (containerPath == null || containerPath.isEmpty()) {
+ return ValidationStatus.error(null);
+ } else if (mountType == null) {
+ return ValidationStatus.error(null);
+ } else if (mountType == MountType.HOST_FILE_SYSTEM
+ && (hostPath == null || hostPath.isEmpty())) {
+ return ValidationStatus.error(null);
+ } else if (mountType == MountType.HOST_FILE_SYSTEM
+ && !new File(hostPath).exists()) {
+ return ValidationStatus
+ .warning("The specified path does not exist on the host."); //$NON-NLS-1$
+ } else if (mountType == MountType.CONTAINER) {
+ final IDockerContainer container = WizardUtils
+ .getContainer(connection, model.getContainerMount());
+ if (container == null) {
+ // just make sure that the dialog cannot complete
+ return ValidationStatus.error(null);
+ }
+ final IDockerContainerInfo selectedContainerInfo = container.info();
+ if (selectedContainerInfo != null
+ && selectedContainerInfo.volumes() != null
+ && !selectedContainerInfo.volumes()
+ .containsKey(model.getContainerPath())) {
+ return ValidationStatus
+ .warning(WizardMessages.getFormattedString(
+ "ContainerDataVolumeDialog.volumeWarning", //$NON-NLS-1$
+ model.getContainerPath()));
+ }
+ }
+ return ValidationStatus.ok();
+ }
+
+ private void setOkButtonEnabled(final boolean enabled) {
+ final Button okButton = getButton(IDialogConstants.OK_ID);
+ // skip if 'OK' button does not exist yet.
+ if (okButton != null) {
+ okButton.setEnabled(enabled);
+ }
+ }
+
+}
diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerLaunchConfigurationDelegate.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerLaunchConfigurationDelegate.java
index ae11435556f..096db2fbed7 100644
--- a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerLaunchConfigurationDelegate.java
+++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerLaunchConfigurationDelegate.java
@@ -10,6 +10,7 @@
*******************************************************************************/
package org.eclipse.cdt.internal.docker.launcher;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -80,7 +81,8 @@ public class ContainerLaunchConfigurationDelegate extends GdbLaunchDelegate
@Override
public void newOutput(String output) {
- if (output.contains(Messages.Gdbserver_up)) {
+ if (output.contains(Messages.Gdbserver_up)
+ || output.contains("gdbserver:")) { //$NON-NLS-1$
started = true;
}
@@ -137,10 +139,16 @@ public class ContainerLaunchConfigurationDelegate extends GdbLaunchDelegate
labels.put("org.eclipse.cdt.project-name", projectName); //$NON-NLS-1$
if (mode.equals(ILaunchManager.RUN_MODE)) {
String commandDir = commandPath.removeLastSegments(1)
- .toString();
+ .toPortableString();
+ String commandString = commandPath.toPortableString();
+
+ if (commandPath.getDevice() != null) {
+ commandDir = "/" + commandDir.replace(':', '/'); //$NON-NLS-1$
+ commandString = "/" + commandString.replace(':', '/'); //$NON-NLS-1$
+ }
StringBuilder b = new StringBuilder();
- b.append(commandPath.toString().trim());
+ b.append(commandString);
String arguments = getProgramArguments(configuration);
if (arguments.trim().length() > 0) {
@@ -154,6 +162,13 @@ public class ContainerLaunchConfigurationDelegate extends GdbLaunchDelegate
.getAttribute(
ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY,
(String) null);
+ if (workingDir != null) {
+ IPath workingPath = new Path(workingDir);
+ if (workingPath.getDevice() != null) {
+ workingDir = "/" + workingPath.toPortableString() //$NON-NLS-1$
+ .replace(':', '/');
+ }
+ }
Map<String, String> envMap = configuration.getAttribute(
ILaunchManager.ATTR_ENVIRONMENT_VARIABLES,
(Map<String, String>) null);
@@ -168,6 +183,18 @@ public class ContainerLaunchConfigurationDelegate extends GdbLaunchDelegate
List<String> additionalDirs = configuration.getAttribute(
ILaunchConstants.ATTR_ADDITIONAL_DIRS,
(List<String>) null);
+ if (additionalDirs != null) {
+ List<String> dirs = new ArrayList<>();
+ for (String additionalDir : additionalDirs) {
+ IPath path = new Path(additionalDir);
+ String dir = path.toPortableString();
+ if (path.getDevice() != null) {
+ dir = "/" + dir.replace(':', '/');
+ }
+ dirs.add(dir);
+ }
+ additionalDirs = dirs;
+ }
String image = configuration.getAttribute(
ILaunchConstants.ATTR_IMAGE, (String) null);
String connectionUri = configuration.getAttribute(
@@ -196,11 +223,18 @@ public class ContainerLaunchConfigurationDelegate extends GdbLaunchDelegate
String gdbserverCommand = configuration.getAttribute(
ILaunchConstants.ATTR_GDBSERVER_COMMAND,
ILaunchConstants.ATTR_GDBSERVER_COMMAND_DEFAULT);
- String commandArguments = ":" + gdbserverPortNumber + " " //$NON-NLS-1$ //$NON-NLS-2$
- + spaceEscapify(commandPath.toString());
+ String commandString = commandPath.toPortableString();
String commandDir = commandPath.removeLastSegments(1)
- .toString();
+ .toPortableString();
+
+ if (commandPath.getDevice() != null) {
+ commandDir = "/" + commandDir.replace(':', '/'); //$NON-NLS-1$
+ commandString = "/" + commandString.replace(':', '/'); //$NON-NLS-1$
+ }
+
+ String commandArguments = ":" + gdbserverPortNumber + " " //$NON-NLS-1$ //$NON-NLS-2$
+ + spaceEscapify(commandString);
StringBuilder b = new StringBuilder();
@@ -217,6 +251,14 @@ public class ContainerLaunchConfigurationDelegate extends GdbLaunchDelegate
.getAttribute(
ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY,
(String) null);
+ if (workingDir != null) {
+ IPath workingPath = new Path(workingDir);
+ if (workingPath.getDevice() != null) {
+ workingDir = "/" + workingPath.toPortableString() //$NON-NLS-1$
+ .replace(':', '/');
+ }
+ }
+
Map<String, String> envMap = configuration.getAttribute(
ILaunchManager.ATTR_ENVIRONMENT_VARIABLES,
(Map<String, String>) null);
@@ -231,6 +273,19 @@ public class ContainerLaunchConfigurationDelegate extends GdbLaunchDelegate
List<String> additionalDirs = configuration.getAttribute(
ILaunchConstants.ATTR_ADDITIONAL_DIRS,
(List<String>) null);
+ if (additionalDirs != null) {
+ List<String> dirs = new ArrayList<>();
+ for (String additionalDir : additionalDirs) {
+ IPath path = new Path(additionalDir);
+ String dir = path.toPortableString();
+ if (path.getDevice() != null) {
+ dir = "/" + dir.replace(':', '/');
+ }
+ dirs.add(dir);
+ }
+ additionalDirs = dirs;
+ }
+
String image = configuration.getAttribute(
ILaunchConstants.ATTR_IMAGE, (String) null);
String connectionUri = configuration.getAttribute(
diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerPropertyTab.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerPropertyTab.java
new file mode 100644
index 00000000000..cb892ea4526
--- /dev/null
+++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerPropertyTab.java
@@ -0,0 +1,1053 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Red Hat Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Red Hat Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.internal.docker.launcher;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvider;
+import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvidersKeeper;
+import org.eclipse.cdt.core.model.CoreModelUtil;
+import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
+import org.eclipse.cdt.core.settings.model.ICMultiConfigDescription;
+import org.eclipse.cdt.core.settings.model.ICResourceDescription;
+import org.eclipse.cdt.core.settings.model.ICTargetPlatformSetting;
+import org.eclipse.cdt.internal.docker.launcher.ContainerPropertyVolumesModel.MountType;
+import org.eclipse.cdt.managedbuilder.buildproperties.IOptionalBuildProperties;
+import org.eclipse.cdt.managedbuilder.core.IConfiguration;
+import org.eclipse.cdt.managedbuilder.core.IMultiConfiguration;
+import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
+import org.eclipse.cdt.managedbuilder.internal.core.Configuration;
+import org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector;
+import org.eclipse.cdt.managedbuilder.ui.properties.AbstractCBuildPropertyTab;
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.IBeanValueProperty;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.property.Properties;
+import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
+import org.eclipse.jface.databinding.viewers.ObservableMapLabelProvider;
+import org.eclipse.jface.databinding.viewers.ViewerSupport;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.linuxtools.docker.core.DockerConnectionManager;
+import org.eclipse.linuxtools.docker.core.IDockerConnection;
+import org.eclipse.linuxtools.docker.core.IDockerConnectionManagerListener;
+import org.eclipse.linuxtools.docker.core.IDockerImage;
+import org.eclipse.linuxtools.docker.core.IDockerImageListener;
+import org.eclipse.linuxtools.internal.docker.ui.wizards.WizardMessages;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.events.VerifyListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+
+@SuppressWarnings("restriction")
+public class ContainerPropertyTab extends AbstractCBuildPropertyTab
+ implements IDockerConnectionManagerListener, IDockerImageListener {
+
+ public final static String VOLUME_SEPARATOR = "|"; //$NON-NLS-1$
+
+ private final static String GNU_ELF_PARSER_ID = "org.eclipse.cdt.core.GNU_ELF"; //$NON-NLS-1$
+ private final static String ELF_PARSER_ID = "org.eclipse.cdt.core.ELF"; //$NON-NLS-1$
+
+ private Combo imageCombo;
+ private Combo connectionSelector;
+ private Button enableButton;
+ private Button addButton;
+ private IDockerConnection connection;
+ private IDockerConnection[] connections;
+ private IDockerImageListener containerTab;
+
+ private String connectionName;
+ private String connectionUri = ""; //$NON-NLS-1$
+
+ private boolean initialEnabled;
+ private String initialConnection;
+ private String initialImageId;
+ private String initialVolumes;
+ private String initialSelectedVolumes;
+
+ private boolean multiChange;
+
+ private List<IDockerImage> displayedImages = new ArrayList<>();
+
+ private IConfiguration iCfg;
+ private ICConfigurationDescription iCfgd;
+
+ private final DataBindingContext dbc = new DataBindingContext();
+ private final ContainerPropertyVolumesModel model;
+
+ private ModifyListener connectionModifyListener = new ModifyListener() {
+
+ @Override
+ public void modifyText(ModifyEvent e) {
+ int index = connectionSelector.getSelectionIndex();
+ if (index < 0) {
+ connection = null;
+ connectionName = "";
+ return;
+ }
+ if (connection != null)
+ connection.removeImageListener(containerTab);
+ connection = connections[index];
+ connectionUri = connection.getUri();
+ if (!connectionName.equals(connection.getName())) {
+ imageCombo.setText("");
+ initialImageId = null;
+ refreshImages();
+ setVolumeControlsEnabled(new Button[] { addButton }, false);
+ }
+ connectionName = connection.getName();
+ setConnection(connectionUri);
+ model.setConnection(connection);
+ }
+
+ };
+
+ public ContainerPropertyTab() {
+ this.containerTab = this;
+ this.model = new ContainerPropertyVolumesModel(
+ (IDockerConnection) null);
+ }
+
+ @Override
+ public void createControls(Composite parent) {
+ super.createControls(parent);
+
+ usercomp.setLayout(new GridLayout(5, false));
+ usercomp.setFont(parent.getFont());
+ GridData gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.horizontalSpan = 1;
+
+ usercomp.setLayoutData(gd);
+
+ enableButton = new Button(usercomp, SWT.CHECK);
+ enableButton.setText(Messages.ContainerPropertyTab_Enable_Msg);
+
+ iCfg = getCfg();
+ iCfgd = getResDesc().getConfiguration();
+
+ gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.horizontalSpan = 5;
+ enableButton.setLayoutData(gd);
+
+ Label connectionSelectorLabel = new Label(usercomp, SWT.NULL);
+ connectionSelectorLabel
+ .setText(Messages.ContainerTab_Connection_Selector_Label);
+ gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.horizontalSpan = 1;
+ gd.grabExcessHorizontalSpace = false;
+ connectionSelectorLabel.setLayoutData(gd);
+
+ connectionSelector = new Combo(usercomp, SWT.BORDER | SWT.READ_ONLY);
+ initializeConnectionSelector();
+ connectionSelector.addModifyListener(connectionModifyListener);
+ // Following is a kludge so that on Linux the Combo is read-only but
+ // has a white background.
+ connectionSelector.addVerifyListener(new VerifyListener() {
+ @Override
+ public void verifyText(VerifyEvent e) {
+ e.doit = false;
+ }
+ });
+ gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.horizontalSpan = 3;
+ gd.grabExcessHorizontalSpace = true;
+ connectionSelector.setLayoutData(gd);
+
+ Label label1 = new Label(usercomp, SWT.NULL);
+ gd = new GridData();
+ gd.horizontalSpan = 1;
+ gd.grabExcessHorizontalSpace = false;
+ label1.setLayoutData(gd);
+
+ Label imageSelectorLabel = new Label(usercomp, SWT.NULL);
+ imageSelectorLabel.setText(Messages.ContainerTab_Image_Selector_Label);
+ gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.horizontalSpan = 1;
+ connectionSelectorLabel.setLayoutData(gd);
+
+ imageCombo = new Combo(usercomp, SWT.DROP_DOWN);
+ gd = new GridData(GridData.FILL_HORIZONTAL);
+ gd.horizontalSpan = 3;
+ gd.grabExcessHorizontalSpace = true;
+ imageCombo.setLayoutData(gd);
+
+ Label label2 = new Label(usercomp, SWT.NULL);
+ gd = new GridData();
+ gd.horizontalSpan = 1;
+ gd.grabExcessHorizontalSpace = false;
+ label2.setLayoutData(gd);
+
+ initializeImageCombo();
+
+ imageCombo.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ setImageId(imageCombo.getText());
+ model.setSelectedImage(
+ displayedImages.get(imageCombo.getSelectionIndex()));
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ }
+
+ });
+
+ initializeEnablementButton();
+ enableButton.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ setControlsEnabled(enableButton.getSelection());
+ setEnablement(enableButton.getSelection());
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ // ignore
+ }
+
+ });
+
+ createVolumeSettingsContainer(usercomp);
+ }
+
+ private void createVolumeSettingsContainer(final Composite container) {
+ final Label volumesLabel = new Label(container, SWT.NONE);
+ volumesLabel.setText(WizardMessages
+ .getString("ImageRunResourceVolVarPage.dataVolumesLabel")); //$NON-NLS-1$
+ GridDataFactory.fillDefaults().grab(false, false).applyTo(volumesLabel);
+ final CheckboxTableViewer dataVolumesTableViewer = createVolumesTable(
+ container);
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP)
+ .grab(true, false).hint(400, 200)
+ .applyTo(dataVolumesTableViewer.getTable());
+ // buttons
+ final Composite buttonsContainers = new Composite(container, SWT.NONE);
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP)
+ .grab(false, false).applyTo(buttonsContainers);
+ GridLayoutFactory.fillDefaults().numColumns(1).margins(0, 0)
+ .spacing(SWT.DEFAULT, 0).applyTo(buttonsContainers);
+
+ addButton = new Button(buttonsContainers, SWT.NONE);
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP)
+ .grab(true, false).applyTo(addButton);
+ addButton.setText(WizardMessages
+ .getString("ImageRunResourceVolVarPage.addButton")); //$NON-NLS-1$
+ addButton.addSelectionListener(onAddDataVolume(dataVolumesTableViewer));
+ if (imageCombo.getText() != null && !imageCombo.getText().equals("")) {
+ setVolumeControlsEnabled(new Button[] { addButton }, true);
+ }
+ final Button editButton = new Button(buttonsContainers, SWT.NONE);
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP)
+ .grab(true, false).applyTo(editButton);
+ editButton.setText(WizardMessages
+ .getString("ImageRunResourceVolVarPage.editButton")); //$NON-NLS-1$
+ editButton
+ .addSelectionListener(onEditDataVolume(dataVolumesTableViewer));
+ editButton.setEnabled(false);
+ final Button removeButton = new Button(buttonsContainers, SWT.NONE);
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP)
+ .grab(true, false).applyTo(removeButton);
+ removeButton.setText(WizardMessages
+ .getString("ImageRunResourceVolVarPage.removeButton")); //$NON-NLS-1$
+ removeButton.addSelectionListener(
+ onRemoveDataVolumes(dataVolumesTableViewer));
+ removeButton.setEnabled(false);
+ // update table content when selected image changes
+ bind(dataVolumesTableViewer, model.getDataVolumes(),
+ BeanProperties.values(DataVolumeModel.class,
+ DataVolumeModel.CONTAINER_PATH, DataVolumeModel.MOUNT,
+ DataVolumeModel.READ_ONLY_VOLUME));
+ dbc.bindSet(
+ ViewersObservables.observeCheckedElements(
+ dataVolumesTableViewer, DataVolumeModel.class),
+ BeanProperties.set(
+ ContainerPropertyVolumesModel.SELECTED_DATA_VOLUMES)
+ .observe(model));
+ // disable the edit and removeButton if the table is empty
+ dataVolumesTableViewer.addSelectionChangedListener(
+ onSelectionChanged(editButton, removeButton));
+
+ initializeVolumesTable();
+ }
+
+ /**
+ * Same as
+ * {@link ViewerSupport#bind(StructuredViewer, IObservableList, org.eclipse.core.databinding.property.value.IValueProperty[])
+ * but with a custom LabelProvider, DataVolumesLabelProvider
+ *
+ * @param viewer
+ * @param input
+ * @param labelProperties
+ */
+ private void bind(final StructuredViewer viewer,
+ final IObservableList input,
+ final IBeanValueProperty[] labelProperties) {
+ final ObservableListContentProvider contentProvider = new ObservableListContentProvider();
+ if (viewer.getInput() != null) {
+ viewer.setInput(null);
+ }
+ viewer.setContentProvider(contentProvider);
+ viewer.setLabelProvider(
+ new DataVolumesLabelProvider(Properties.observeEach(
+ contentProvider.getKnownElements(), labelProperties)));
+ if (input != null) {
+ viewer.setInput(input);
+ }
+
+ }
+
+ private ISelectionChangedListener onSelectionChanged(
+ final Button... targetButtons) {
+ return e -> {
+ if (e.getSelection().isEmpty()) {
+ setVolumeControlsEnabled(targetButtons, false);
+ } else {
+ setVolumeControlsEnabled(targetButtons, true);
+ }
+ };
+ }
+
+ private static void setVolumeControlsEnabled(final Control[] controls,
+ final boolean enabled) {
+ for (Control control : controls) {
+ if (control != null) {
+ control.setEnabled(enabled);
+ }
+ }
+ }
+
+ private SelectionListener onAddDataVolume(
+ final CheckboxTableViewer dataVolumesTableViewer) {
+ return SelectionListener.widgetSelectedAdapter(e -> {
+ final ContainerDataVolumeDialog dialog = new ContainerDataVolumeDialog(
+ Display.getDefault().getActiveShell(),
+ model.getConnection());
+ dialog.create();
+ if (dialog.open() == IDialogConstants.OK_ID) {
+ final DataVolumeModel dataVolume = dialog.getDataVolume();
+ dataVolume.setSelected(true);
+ model.getDataVolumes().add(dataVolume);
+ model.getSelectedDataVolumes().add(dataVolume);
+ dataVolumesTableViewer.setChecked(dataVolume, true);
+ setVolumes();
+ }
+ });
+ }
+
+ private SelectionListener onEditDataVolume(
+ final CheckboxTableViewer dataVolumesTableViewer) {
+ return SelectionListener.widgetSelectedAdapter(e -> {
+ final IStructuredSelection selection = (IStructuredSelection) dataVolumesTableViewer
+ .getSelection();
+ if (selection.isEmpty()) {
+ return;
+ }
+ final DataVolumeModel selectedDataVolume = (DataVolumeModel) selection
+ .getFirstElement();
+ final ContainerDataVolumeDialog dialog = new ContainerDataVolumeDialog(
+ Display.getDefault().getActiveShell(),
+ model.getConnection(), selectedDataVolume);
+ dialog.create();
+ if (dialog.open() == IDialogConstants.OK_ID) {
+ final DataVolumeModel dialogDataVolume = dialog.getDataVolume();
+ selectedDataVolume.setContainerMount(
+ dialogDataVolume.getContainerMount());
+ selectedDataVolume
+ .setMountType(dialogDataVolume.getMountType());
+ selectedDataVolume
+ .setHostPathMount(dialogDataVolume.getHostPathMount());
+ selectedDataVolume.setContainerMount(
+ dialogDataVolume.getContainerMount());
+ selectedDataVolume.setReadOnly(dialogDataVolume.isReadOnly());
+ model.getSelectedDataVolumes().add(selectedDataVolume);
+ dataVolumesTableViewer.setChecked(selectedDataVolume, true);
+ setVolumes();
+ }
+ });
+ }
+
+ private SelectionListener onRemoveDataVolumes(
+ final TableViewer dataVolumesTableViewer) {
+ return SelectionListener.widgetSelectedAdapter(e -> {
+ final IStructuredSelection selection = dataVolumesTableViewer
+ .getStructuredSelection();
+ for (@SuppressWarnings("unchecked")
+ Iterator<DataVolumeModel> iterator = selection.iterator(); iterator
+ .hasNext();) {
+ final DataVolumeModel volume = iterator.next();
+ model.removeDataVolume(volume);
+ model.getSelectedDataVolumes().remove(volume);
+ }
+ setVolumes();
+ });
+ }
+
+ private CheckboxTableViewer createVolumesTable(final Composite container) {
+ final Table table = new Table(container, SWT.CHECK | SWT.BORDER
+ | SWT.FULL_SELECTION | SWT.V_SCROLL | SWT.H_SCROLL);
+ final CheckboxTableViewer tableViewer = new CheckboxTableViewer(table);
+ table.setHeaderVisible(true);
+ table.setLinesVisible(true);
+ dbc.bindSet(
+ ViewersObservables.observeCheckedElements(tableViewer,
+ DataVolumeModel.class),
+ BeanProperties.set(
+ ContainerPropertyVolumesModel.SELECTED_DATA_VOLUMES)
+ .observe(model));
+ addTableViewerColumn(tableViewer,
+ WizardMessages.getString(
+ "ImageRunResourceVolVarPage.containerPathColumn"), //$NON-NLS-1$
+ 180);
+ addTableViewerColumn(tableViewer,
+ WizardMessages
+ .getString("ImageRunResourceVolVarPage.mountColumn"), //$NON-NLS-1$
+ 180);
+ addTableViewerColumn(tableViewer,
+ WizardMessages
+ .getString("ImageRunResourceVolVarPage.readonlyColumn"), //$NON-NLS-1$
+ 60);
+ return tableViewer;
+ }
+
+ private TableViewerColumn addTableViewerColumn(
+ final TableViewer tableViewer,
+ final String title, final int width) {
+ final TableViewerColumn viewerColumn = new TableViewerColumn(
+ tableViewer, SWT.NONE);
+ final TableColumn column = viewerColumn.getColumn();
+ if (title != null) {
+ column.setText(title);
+ }
+ column.setWidth(width);
+ return viewerColumn;
+ }
+
+ private static final class DataVolumesLabelProvider
+ extends ObservableMapLabelProvider {
+
+ private Image CONTAINER_IMAGE = SWTImagesFactory.DESC_CONTAINER
+ .createImage();
+ private Image FOLDER_CLOSED_IMAGE = SWTImagesFactory.DESC_FOLDER_CLOSED
+ .createImage();
+ private Image FILE_IMAGE = SWTImagesFactory.DESC_FILE.createImage();
+
+ public DataVolumesLabelProvider(final IObservableMap[] attributeMaps) {
+ super(attributeMaps);
+ }
+
+ @Override
+ public void dispose() {
+ CONTAINER_IMAGE.dispose();
+ FOLDER_CLOSED_IMAGE.dispose();
+ FILE_IMAGE.dispose();
+ super.dispose();
+ }
+
+ @Override
+ public Image getColumnImage(Object element, int columnIndex) {
+ final DataVolumeModel dataVolume = ((DataVolumeModel) element);
+ if (dataVolume.getMountType() != null && columnIndex == 1) {
+ switch (dataVolume.getMountType()) {
+ case CONTAINER:
+ return CONTAINER_IMAGE;
+ case HOST_FILE_SYSTEM:
+ final File hostFile = new File(dataVolume.getMount());
+ if (!hostFile.exists() || hostFile.isDirectory()) {
+ return FOLDER_CLOSED_IMAGE;
+ } else {
+ return FILE_IMAGE;
+ }
+ default:
+ return null;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String getColumnText(Object element, int columnIndex) {
+ final DataVolumeModel dataVolume = ((DataVolumeModel) element);
+ switch (columnIndex) {
+ case 0:
+ return dataVolume.getContainerPath();
+ case 1:
+ return dataVolume.getMount();
+ case 2:
+ if (dataVolume.getMountType() != MountType.HOST_FILE_SYSTEM) {
+ return null;
+ } else if (dataVolume.isReadOnly()) {
+ return WizardMessages
+ .getString("ImageRunResourceVolVarPage.true"); //$NON-NLS-1$
+ }
+ return WizardMessages
+ .getString("ImageRunResourceVolVarPage.false"); //$NON-NLS-1$
+ default:
+ return null;
+ }
+ }
+ }
+
+ private void setVolumes() {
+ StringBuffer buffer = new StringBuffer();
+ String separator = ""; //$NON-NLS-1$
+ for (DataVolumeModel volume : model.getDataVolumes()) {
+ buffer.append(separator);
+ buffer.append(volume.toString());
+ separator = VOLUME_SEPARATOR;
+ }
+ StringBuffer selectedBuffer = new StringBuffer();
+ separator = ""; //$NON-NLS-1$
+ for (DataVolumeModel volume : model.getSelectedDataVolumes()) {
+ selectedBuffer.append(separator);
+ selectedBuffer.append(volume.toString());
+ separator = VOLUME_SEPARATOR;
+ }
+ if (iCfg instanceof IMultiConfiguration) {
+ IConfiguration[] cfs = (IConfiguration[]) ((IMultiConfiguration) iCfg)
+ .getItems();
+ for (int i = 0; i < cfs.length; i++) {
+ IConfiguration cfg = cfs[i];
+ IOptionalBuildProperties p = cfg.getOptionalBuildProperties();
+ p.setProperty(ContainerCommandLauncher.VOLUMES_ID,
+ buffer.toString());
+ p.setProperty(ContainerCommandLauncher.SELECTED_VOLUMES_ID,
+ selectedBuffer.toString());
+ }
+ } else {
+ IOptionalBuildProperties p = iCfg.getOptionalBuildProperties();
+ p.setProperty(ContainerCommandLauncher.VOLUMES_ID,
+ buffer.toString());
+ p.setProperty(ContainerCommandLauncher.SELECTED_VOLUMES_ID,
+ selectedBuffer.toString());
+ }
+ }
+
+ private void setEnablement(boolean enabled) {
+ if (iCfg instanceof IMultiConfiguration) {
+ IConfiguration[] cfs = (IConfiguration[]) ((IMultiConfiguration) iCfg)
+ .getItems();
+ for (int i = 0; i < cfs.length; i++) {
+ IConfiguration cfg = cfs[i];
+ IOptionalBuildProperties p = cfg.getOptionalBuildProperties();
+ p.setProperty(ContainerCommandLauncher.CONTAINER_BUILD_ENABLED,
+ Boolean.toString(enableButton.getSelection()));
+ }
+ } else {
+ IOptionalBuildProperties p = iCfg.getOptionalBuildProperties();
+ p.setProperty(ContainerCommandLauncher.CONTAINER_BUILD_ENABLED,
+ Boolean.toString(enableButton.getSelection()));
+ }
+ // if enabled, make sure we have ELF binary parsers specified
+ if (enabled) {
+ String[] ids = CoreModelUtil
+ .getBinaryParserIds(page.getCfgsEditable());
+ List<String> idList = new ArrayList<>(Arrays.asList(ids));
+ if (!idList.contains(GNU_ELF_PARSER_ID)) {
+ idList.add(GNU_ELF_PARSER_ID);
+ }
+ if (!idList.contains(ELF_PARSER_ID)) {
+ idList.add(ELF_PARSER_ID);
+ }
+ CoreModelUtil.setBinaryParserIds(page.getCfgsEditable(),
+ idList.toArray(new String[0]));
+ }
+ }
+
+ private void setImageId(String imageId) {
+ if (iCfg instanceof IMultiConfiguration) {
+ IConfiguration[] cfs = (IConfiguration[]) ((IMultiConfiguration) iCfg)
+ .getItems();
+ for (int i = 0; i < cfs.length; i++) {
+ IConfiguration cfg = cfs[i];
+ IOptionalBuildProperties p = cfg.getOptionalBuildProperties();
+ p.setProperty(ContainerCommandLauncher.IMAGE_ID, imageId);
+ }
+ } else {
+ IOptionalBuildProperties p = iCfg.getOptionalBuildProperties();
+ p.setProperty(ContainerCommandLauncher.IMAGE_ID, imageId);
+ }
+ }
+
+ private void setConnection(String uri) {
+ if (iCfg instanceof IMultiConfiguration) {
+ IConfiguration[] cfs = (IConfiguration[]) ((IMultiConfiguration) iCfg)
+ .getItems();
+ for (int i = 0; i < cfs.length; i++) {
+ IConfiguration cfg = cfs[i];
+ IOptionalBuildProperties p = cfg.getOptionalBuildProperties();
+ p.setProperty(ContainerCommandLauncher.CONNECTION_ID, uri);
+ }
+ } else {
+ IOptionalBuildProperties p = iCfg.getOptionalBuildProperties();
+ p.setProperty(ContainerCommandLauncher.CONNECTION_ID, uri);
+ }
+ }
+
+ private void setControlsEnabled(boolean enabled) {
+ imageCombo.setEnabled(enabled);
+ connectionSelector.setEnabled(enabled);
+ setVolumeControlsEnabled(new Button[] { addButton }, enabled);
+ }
+
+ private void initializeEnablementButton() {
+ initialEnabled = false;
+ IOptionalBuildProperties properties = iCfg.getOptionalBuildProperties();
+ String savedEnabled = properties
+ .getProperty(ContainerCommandLauncher.CONTAINER_BUILD_ENABLED);
+ if (savedEnabled != null) {
+ initialEnabled = Boolean
+ .parseBoolean(savedEnabled);
+ }
+ enableButton.setSelection(initialEnabled);
+ setControlsEnabled(initialEnabled);
+ }
+
+ private void initializeConnectionSelector() {
+ int defaultIndex = -1;
+ initialConnection = null;
+ IOptionalBuildProperties properties = iCfg.getOptionalBuildProperties();
+ String id = properties
+ .getProperty(ContainerCommandLauncher.CONNECTION_ID);
+ if (id != null) {
+ initialConnection = id;
+ }
+ connections = DockerConnectionManager.getInstance().getConnections();
+ if (connections.length == 0) {
+ // setErrorMessage(Messages.ContainerTab_Error_No_Connections);
+ return;
+ }
+ String[] connectionNames = new String[connections.length];
+ for (int i = 0; i < connections.length; ++i) {
+ connectionNames[i] = connections[i].getName();
+ if (connections[i].getUri().equals(initialConnection))
+ defaultIndex = i;
+ }
+ if (defaultIndex < 0) {
+ initialEnabled = false;
+ defaultIndex = 0;
+ }
+ connectionSelector.setItems(connectionNames);
+ if (connections.length > 0) {
+ connectionSelector.select(defaultIndex);
+ connection = connections[defaultIndex];
+ connectionName = connection.getName();
+ connectionUri = connection.getUri();
+ initialConnection = connectionUri;
+ model.setConnection(connection);
+ }
+ }
+
+ private void refreshImages() {
+ if (connection != null) {
+ java.util.List<IDockerImage> images = connection.getImages();
+ if (images == null || images.size() == 0) {
+ // setsetErrorMessage(Messages.ContainerTab_Error_No_Images);
+ return;
+ }
+ connection.removeImageListener(containerTab);
+ ArrayList<String> imageNames = new ArrayList<String>();
+ displayedImages = new ArrayList<>();
+ for (IDockerImage image : images) {
+ java.util.List<String> tags = image.repoTags();
+ if (tags != null) {
+ for (String tag : tags) {
+ if (!tag.equals("<none>:<none>")) { //$NON-NLS-1$
+ imageNames.add(tag);
+ displayedImages.add(image);
+ }
+ }
+ }
+ }
+ imageCombo.setItems(imageNames.toArray(new String[0]));
+ if (initialImageId != null) {
+ int index = imageCombo.indexOf(initialImageId);
+ if (index > -1) {
+ imageCombo.select(index);
+ model.setSelectedImage(displayedImages.get(index));
+ setVolumeControlsEnabled(new Button[] { addButton }, true);
+ } else {
+ }
+ }
+ connection.addImageListener(containerTab);
+ }
+
+ }
+
+ private void initializeImageCombo() {
+ initialImageId = null;
+ IOptionalBuildProperties properties = iCfg.getOptionalBuildProperties();
+ String id = properties.getProperty(ContainerCommandLauncher.IMAGE_ID);
+ if (id != null) {
+ initialImageId = id;
+ }
+ refreshImages();
+ }
+
+ private void initializeVolumesTable() {
+ model.clearDataVolumes();
+ int imageSelectionIndex = imageCombo.getSelectionIndex();
+ if (imageSelectionIndex >= 0
+ && imageSelectionIndex < displayedImages.size()) {
+ model.setSelectedImage(displayedImages.get(imageSelectionIndex));
+ }
+
+ IOptionalBuildProperties properties = iCfg.getOptionalBuildProperties();
+ initialVolumes = properties
+ .getProperty(ContainerCommandLauncher.VOLUMES_ID);
+ Map<String, DataVolumeModel> volumeMap = parseVolumes(initialVolumes);
+ initialSelectedVolumes = properties
+ .getProperty(ContainerCommandLauncher.SELECTED_VOLUMES_ID);
+ Map<String, DataVolumeModel> selectedVolumeMap = parseVolumes(
+ initialSelectedVolumes);
+ Set<DataVolumeModel> selectedVolumes = new HashSet<>();
+ for (DataVolumeModel dvm : selectedVolumeMap.values()) {
+ // we need selected volumes to be volumes that are in the volumes
+ // collection, so just replace them in the volumes Map so they will
+ // be
+ // the same objects
+ volumeMap.put(dvm.getContainerPath(), dvm);
+ selectedVolumes.add(dvm);
+ }
+ model.setDataVolumes(volumeMap.values());
+ model.setSelectedDataVolumes(selectedVolumes);
+ }
+
+ private Map<String, DataVolumeModel> parseVolumes(String volumesString) {
+ Map<String, DataVolumeModel> volumeMap = new HashMap<>();
+ if (volumesString != null && !volumesString.equals("")) { //$NON-NLS-1$
+ String[] volumes = volumesString
+ .split("[" + VOLUME_SEPARATOR + "]"); //$NON-NLS-1$ //$NON-NLS-2$
+ for (String volume : volumes) {
+ if (volume != null && !volume.equals("")) { //$NON-NLS-1$
+ DataVolumeModel dataVolume = DataVolumeModel
+ .parseString(volume);
+ volumeMap.put(dataVolume.getContainerPath(), dataVolume);
+ }
+ }
+ }
+ return volumeMap;
+ }
+
+ @Override
+ protected void performApply(ICResourceDescription src,
+ ICResourceDescription dst) {
+ setVolumes();
+ boolean needToRecalculate = false;
+ ICConfigurationDescription defaultCfg = null;
+ if (page.isMultiCfg()) {
+ ICMultiConfigDescription mc1 = (ICMultiConfigDescription) src
+ .getConfiguration();
+ ICMultiConfigDescription mc2 = (ICMultiConfigDescription) dst
+ .getConfiguration();
+ ICConfigurationDescription[] cds1 = (ICConfigurationDescription[]) mc1
+ .getItems();
+ ICConfigurationDescription[] cds2 = (ICConfigurationDescription[]) mc2
+ .getItems();
+ defaultCfg = cds1[0];
+ for (int i = 0; i < cds1.length; i++)
+ needToRecalculate |= applyToCfg(cds1[i], cds2[i]);
+ } else {
+ defaultCfg = src.getConfiguration();
+ needToRecalculate = applyToCfg(src.getConfiguration(),
+ dst.getConfiguration());
+ }
+ if (needToRecalculate) {
+ recalculateSpecs(defaultCfg, true);
+ }
+ }
+
+ private boolean applyToCfg(ICConfigurationDescription c1,
+ ICConfigurationDescription c2) {
+ Configuration cfg01 = (Configuration) getCfg(c1);
+ Configuration cfg02 = (Configuration) getCfg(c2);
+ IOptionalBuildProperties prop1 = cfg01.getOptionalBuildProperties();
+ IOptionalBuildProperties prop2 = cfg02.getOptionalBuildProperties();
+ boolean needToRecalculate = false;
+
+ ICTargetPlatformSetting tps = c1.getTargetPlatformSetting();
+ String[] pids = tps.getBinaryParserIds();
+ ICTargetPlatformSetting tps2 = c2.getTargetPlatformSetting();
+ tps2.setBinaryParserIds(pids);
+
+ String enablementProperty = prop1
+ .getProperty(ContainerCommandLauncher.CONTAINER_BUILD_ENABLED);
+ String enablementProperty2 = prop2
+ .getProperty(ContainerCommandLauncher.CONTAINER_BUILD_ENABLED);
+ if (enablementProperty != null
+ && !enablementProperty.equals(enablementProperty2)) {
+ needToRecalculate = true;
+ }
+ prop2.setProperty(ContainerCommandLauncher.CONTAINER_BUILD_ENABLED,
+ enablementProperty);
+
+ String connectionProperty = prop1
+ .getProperty(ContainerCommandLauncher.CONNECTION_ID);
+ String connectionProperty2 = prop2
+ .getProperty(ContainerCommandLauncher.CONNECTION_ID);
+ if (connectionProperty != null
+ && !connectionProperty.equals(connectionProperty2)) {
+ needToRecalculate = true;
+ }
+ prop2.setProperty(ContainerCommandLauncher.CONNECTION_ID,
+ connectionProperty);
+
+ String imageProperty = prop1
+ .getProperty(ContainerCommandLauncher.IMAGE_ID);
+ String imageProperty2 = prop2
+ .getProperty(ContainerCommandLauncher.IMAGE_ID);
+ if (imageProperty != null && !imageProperty.equals(imageProperty2)) {
+ needToRecalculate = true;
+ }
+ prop2.setProperty(ContainerCommandLauncher.IMAGE_ID, imageProperty);
+
+ String volumesProperty = prop1
+ .getProperty(ContainerCommandLauncher.VOLUMES_ID);
+ prop2.setProperty(ContainerCommandLauncher.VOLUMES_ID, volumesProperty);
+
+ String selectedVolumesProperty = prop1
+ .getProperty(ContainerCommandLauncher.SELECTED_VOLUMES_ID);
+ prop2.setProperty(ContainerCommandLauncher.SELECTED_VOLUMES_ID,
+ selectedVolumesProperty);
+
+ return needToRecalculate;
+ }
+
+
+ protected void recalculateSpecs(ICConfigurationDescription cfgd,
+ boolean performingApply) {
+ IConfiguration cfg = getCfg(cfgd);
+ IOptionalBuildProperties properties = cfg.getOptionalBuildProperties();
+ initialEnabled = Boolean.parseBoolean(properties
+ .getProperty(ContainerCommandLauncher.CONTAINER_BUILD_ENABLED));
+ initialConnection = properties
+ .getProperty(ContainerCommandLauncher.CONNECTION_ID);
+ initialImageId = properties
+ .getProperty(ContainerCommandLauncher.IMAGE_ID);
+ initialVolumes = properties
+ .getProperty(ContainerCommandLauncher.VOLUMES_ID);
+ initialSelectedVolumes = properties
+ .getProperty(ContainerCommandLauncher.SELECTED_VOLUMES_ID);
+ List<ILanguageSettingsProvider> providers = ((ILanguageSettingsProvidersKeeper) cfgd)
+ .getLanguageSettingProviders();
+ for (ILanguageSettingsProvider provider : providers) {
+ if (provider instanceof GCCBuiltinSpecsDetector) {
+ GCCBuiltinSpecsDetector d = (GCCBuiltinSpecsDetector) provider;
+ // force recalculation of gcc include path
+ d.clear();
+ if (performingApply) {
+ d.handleEvent(null);
+ }
+ // final IProject project = getProject();
+ // CCorePlugin.getIndexManager().reindex(CoreModel.getDefault().create(project));
+ }
+ }
+ }
+
+
+ @Override
+ protected void performOK() {
+ boolean needToRecalculate = false;
+ setVolumes();
+ if (iCfg instanceof IMultiConfiguration) {
+ needToRecalculate = multiChange;
+ } else {
+ IOptionalBuildProperties p = iCfg.getOptionalBuildProperties();
+ if (initialEnabled != Boolean.parseBoolean(p.getProperty(
+ ContainerCommandLauncher.CONTAINER_BUILD_ENABLED))) {
+ needToRecalculate = true;
+ } else if (initialEnabled == true) {
+ if (!initialConnection.equals(
+ p.getProperty(ContainerCommandLauncher.CONNECTION_ID))
+ || !initialImageId.equals(p.getProperty(
+ ContainerCommandLauncher.IMAGE_ID))) {
+ needToRecalculate = true;
+ }
+ }
+ }
+ if (needToRecalculate) {
+ recalculateSpecs(
+ ManagedBuildManager.getDescriptionForConfiguration(iCfg),
+ false);
+ }
+ }
+
+ @Override
+ protected void performDefaults() {
+ if (iCfg instanceof IMultiConfiguration) {
+ IConfiguration[] cfs = (IConfiguration[]) ((IMultiConfiguration) iCfg)
+ .getItems();
+ for (int i = 0; i < cfs.length; i++) {
+ IOptionalBuildProperties props = cfs[i]
+ .getOptionalBuildProperties();
+ props.setProperty(
+ ContainerCommandLauncher.CONTAINER_BUILD_ENABLED,
+ Boolean.toString(false));
+ if (connections.length > 0) {
+ props.setProperty(ContainerCommandLauncher.CONNECTION_ID,
+ connections[0].getUri());
+ } else {
+ props.setProperty(ContainerCommandLauncher.CONNECTION_ID,
+ null);
+ }
+ props.setProperty(ContainerCommandLauncher.IMAGE_ID, null);
+ props.setProperty(ContainerCommandLauncher.VOLUMES_ID, null);
+ props.setProperty(ContainerCommandLauncher.SELECTED_VOLUMES_ID,
+ null);
+ }
+ } else {
+ IOptionalBuildProperties props = iCfg.getOptionalBuildProperties();
+ props.setProperty(ContainerCommandLauncher.CONTAINER_BUILD_ENABLED,
+ Boolean.toString(false));
+ if (connections.length > 0) {
+ props.setProperty(ContainerCommandLauncher.CONNECTION_ID,
+ connections[0].getUri());
+ } else {
+ props.setProperty(ContainerCommandLauncher.CONNECTION_ID, null);
+ }
+ props.setProperty(ContainerCommandLauncher.IMAGE_ID, null);
+ }
+ initialEnabled = false;
+ initialConnection = null;
+ initialImageId = null;
+ initialVolumes = null;
+ initialSelectedVolumes = null;
+ if (connections.length > 0) {
+ connectionSelector.select(0);
+ }
+ imageCombo.setText(""); //$NON-NLS-1$
+ model.setDataVolumes(null);
+ model.setSelectedDataVolumes(null);
+ enableButton.setSelection(false);
+ setControlsEnabled(false);
+ }
+
+ @Override
+ public void updateData(ICResourceDescription cfgd) {
+ if (cfgd == null)
+ return;
+ iCfg = getCfg(cfgd.getConfiguration());
+ iCfgd = cfgd.getConfiguration();
+
+ multiChange = false;
+
+ initializeConnectionSelector();
+ initializeImageCombo();
+ initializeEnablementButton();
+ initializeVolumesTable();
+ }
+
+ @Override
+ protected void updateButtons() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void changeEvent(IDockerConnection changedConnection, int type) {
+ String currUri = null;
+ int currIndex = 0;
+ connections = DockerConnectionManager.getInstance().getConnections();
+ if (connection != null) {
+ currUri = connection.getUri();
+ currIndex = connectionSelector.getSelectionIndex();
+ }
+ String[] connectionNames = new String[connections.length];
+ int index = 0;
+ for (int i = 0; i < connections.length; ++i) {
+ connectionNames[i] = connections[i].getName();
+ if (connections[i].getUri().equals(currUri))
+ index = i;
+ }
+ if (type == IDockerConnectionManagerListener.RENAME_EVENT) {
+ index = currIndex; // no change in connection displayed
+ }
+ connectionSelector.removeModifyListener(connectionModifyListener);
+ connectionSelector.setItems(connectionNames);
+ if (connectionNames.length > 0) {
+ connectionSelector.setText(connectionNames[index]);
+ connection = connections[index];
+ model.setConnection(connection);
+ connectionUri = connection.getUri();
+ } else {
+ connection = null;
+ model.setConnection(null);
+ model.setSelectedImage(null);
+ connectionUri = "";
+ connectionSelector.setText("");
+ }
+ connectionSelector.addModifyListener(connectionModifyListener);
+ }
+
+ @Override
+ public void listChanged(IDockerConnection c,
+ java.util.List<IDockerImage> list) {
+ final IDockerImage[] finalList = list.toArray(new IDockerImage[0]);
+ if (c.getName().equals(connection.getName())) {
+ Display.getDefault().syncExec(new Runnable() {
+ @Override
+ public void run() {
+ connection.removeImageListener(containerTab);
+ ArrayList<String> imageNames = new ArrayList<String>();
+ displayedImages = new ArrayList<>();
+ for (IDockerImage image : finalList) {
+ java.util.List<String> tags = image.repoTags();
+ if (tags != null) {
+ for (String tag : tags) {
+ imageNames.add(tag);
+ displayedImages.add(image);
+ }
+ }
+ }
+ if (!imageCombo.isDisposed())
+ imageCombo.setItems(imageNames.toArray(new String[0]));
+ connection.addImageListener(containerTab);
+ }
+
+ });
+ }
+ }
+
+}
diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerPropertyVolumesModel.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerPropertyVolumesModel.java
new file mode 100644
index 00000000000..ad4ccbf6cba
--- /dev/null
+++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerPropertyVolumesModel.java
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 Red Hat.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Red Hat - Initial Contribution
+ *******************************************************************************/
+package org.eclipse.cdt.internal.docker.launcher;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.linuxtools.docker.core.DockerException;
+import org.eclipse.linuxtools.docker.core.IDockerConnection;
+import org.eclipse.linuxtools.docker.core.IDockerImage;
+import org.eclipse.linuxtools.docker.core.IDockerImageInfo;
+
+/**
+ * Databinding model for the {@link ContainerPropertyTab}
+ *
+ */
+public class ContainerPropertyVolumesModel
+ extends BaseDatabindingModel {
+
+ public enum MountType {
+ NONE, HOST_FILE_SYSTEM, CONTAINER;
+ }
+
+
+ public static final String DATA_VOLUMES = "dataVolumes"; //$NON-NLS-1$
+
+ public static final String SELECTED_DATA_VOLUMES = "selectedDataVolumes"; //$NON-NLS-1$
+
+ private IDockerConnection connection;
+
+ private IDockerImageInfo imageInfo = null;
+
+ private Set<DataVolumeModel> selectedDataVolumes = new HashSet<>();
+
+ private WritableList<DataVolumeModel> dataVolumes = new WritableList<>();
+
+ private List<DataVolumeModel> previousVolumes = new ArrayList<>();
+
+ private IDockerImage selectedImage;
+
+ public ContainerPropertyVolumesModel(
+ final IDockerConnection connection) {
+ this.connection = connection;
+ }
+
+ public ContainerPropertyVolumesModel(
+ final IDockerImage selectedImage) throws DockerException {
+ this(selectedImage.getConnection());
+ this.selectedImage = selectedImage;
+ }
+
+ public void setConnection(IDockerConnection connection) {
+ this.connection = connection;
+ setSelectedImage(null);
+ }
+
+ public IDockerConnection getConnection() {
+ return connection;
+ }
+
+ /**
+ * Refreshes the list of Volumes to display in the for the given
+ *
+ * @param selectedImage
+ */
+ public void setSelectedImage(final IDockerImage selectedImage) {
+ if (this.selectedImage == null
+ || !this.selectedImage.equals(selectedImage)) {
+ this.selectedImage = selectedImage;
+ if (selectedImage != null) {
+ this.imageInfo = selectedImage.getConnection()
+ .getImageInfo(selectedImage.id());
+ if (this.imageInfo.config() != null
+ && this.imageInfo.config().volumes() != null) {
+ for (DataVolumeModel dvm : previousVolumes) {
+ removeDataVolume(dvm);
+ selectedDataVolumes.remove(dvm);
+ }
+ final List<DataVolumeModel> volumes = new ArrayList<>();
+ for (String volume : this.imageInfo.config().volumes()) {
+ volumes.add(new DataVolumeModel(volume));
+ }
+ setDataVolumes(volumes);
+ previousVolumes = volumes;
+ }
+ } else {
+ setDataVolumes(Collections.<DataVolumeModel> emptyList());
+ }
+ }
+
+ }
+
+ public IDockerImage getSelectedImage() {
+ return selectedImage;
+ }
+
+ public IDockerImageInfo getSelectedImageInfo() {
+ return imageInfo;
+ }
+
+ public WritableList<DataVolumeModel> getDataVolumes() {
+ return dataVolumes;
+ }
+
+ public void setDataVolumes(final Collection<DataVolumeModel> volumes) {
+ if (volumes != null) {
+ this.dataVolumes.addAll(volumes);
+ }
+ }
+
+ public void clearDataVolumes() {
+ this.dataVolumes.clear();
+ }
+
+ public void removeDataVolume(final DataVolumeModel dataVolume) {
+ this.dataVolumes.remove(dataVolume);
+ }
+
+ public Set<DataVolumeModel> getSelectedDataVolumes() {
+ return selectedDataVolumes;
+ }
+
+ public void setSelectedDataVolumes(
+ final Set<DataVolumeModel> selectedDataVolumes) {
+ firePropertyChange(SELECTED_DATA_VOLUMES, this.selectedDataVolumes,
+ this.selectedDataVolumes = selectedDataVolumes);
+ }
+
+}
diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerTab.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerTab.java
index 1786646846f..0d69db41fdd 100644
--- a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerTab.java
+++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerTab.java
@@ -517,7 +517,8 @@ public class ContainerTab extends AbstractLaunchConfigurationTab implements
return SWTImagesFactory.get(SWTImagesFactory.IMG_CONTAINER);
}
- public void changeEvent(int type) {
+ @Override
+ public void changeEvent(IDockerConnection changedConnection, int type) {
String currUri = null;
int currIndex = 0;
connections = DockerConnectionManager.getInstance().getConnections();
@@ -549,10 +550,6 @@ public class ContainerTab extends AbstractLaunchConfigurationTab implements
connectionSelector.addModifyListener(connectionModifyListener);
}
- public void changeEvent(IDockerConnection connection, int event) {
- changeEvent(event);
- }
-
public void listChanged(IDockerConnection c,
java.util.List<IDockerImage> list) {
final IDockerImage[] finalList = list.toArray(new IDockerImage[0]);
diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/DataVolumeModel.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/DataVolumeModel.java
new file mode 100644
index 00000000000..d3e5816d18b
--- /dev/null
+++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/DataVolumeModel.java
@@ -0,0 +1,347 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Red Hat.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Red Hat - Initial Contribution
+ *******************************************************************************/
+package org.eclipse.cdt.internal.docker.launcher;
+
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.cdt.internal.docker.launcher.ContainerPropertyVolumesModel.MountType;
+import org.eclipse.core.runtime.Platform;
+
+/**
+ * Data binding model for container data volumes
+ *
+ */
+public class DataVolumeModel extends BaseDatabindingModel
+ implements Comparable<DataVolumeModel> {
+
+ private static final String SEPARATOR = ":"; //$NON-NLS-1$
+
+ public static final String CONTAINER_PATH = "containerPath"; //$NON-NLS-1$
+
+ public static final String MOUNT_TYPE = "mountType"; //$NON-NLS-1$
+
+ public static final String MOUNT = "mount"; //$NON-NLS-1$
+
+ public static final String HOST_PATH_MOUNT = "hostPathMount"; //$NON-NLS-1$
+
+ public static final String READ_ONLY_VOLUME = "readOnly"; //$NON-NLS-1$
+
+ public static final String CONTAINER_MOUNT = "containerMount"; //$NON-NLS-1$
+
+ public static final String SELECTED = "selected"; //$NON-NLS-1$
+
+ private final String id = UUID.randomUUID().toString();
+
+ private String containerPath;
+
+ private MountType mountType;
+
+ private String mount;
+
+ private String hostPathMount;
+
+ private String containerMount;
+
+ private boolean readOnly = false;
+
+ private boolean selected;
+
+
+ /**
+ * Default constructor
+ */
+ public DataVolumeModel() {
+ }
+
+ /**
+ * Constructor
+ *
+ * @param containerPath
+ * the container path
+ */
+ public DataVolumeModel(final String containerPath) {
+ this.containerPath = containerPath;
+ this.mountType = MountType.NONE;
+ }
+
+ public DataVolumeModel(final String containerPath, final String hostPath,
+ final boolean readOnly) {
+ this.containerPath = containerPath;
+ this.mountType = MountType.HOST_FILE_SYSTEM;
+ this.hostPathMount = hostPath;
+ this.mount = this.hostPathMount;
+ this.readOnly = readOnly;
+ }
+
+ public DataVolumeModel(final DataVolumeModel selectedDataVolume) {
+ this.containerPath = selectedDataVolume.getContainerPath();
+ this.mountType = selectedDataVolume.getMountType();
+ if (this.mountType != null) {
+ switch (this.mountType) {
+ case CONTAINER:
+ this.containerMount = selectedDataVolume.getMount();
+ break;
+ case HOST_FILE_SYSTEM:
+ this.hostPathMount = selectedDataVolume.getMount();
+ this.readOnly = selectedDataVolume.isReadOnly();
+ break;
+ case NONE:
+ break;
+ }
+ } else {
+ this.mountType = MountType.NONE;
+ }
+ }
+
+ /**
+ * Create a DataVolumeModel from a toString() output.
+ *
+ * @param fromString
+ * @return DataVolumeModel
+ */
+ public static DataVolumeModel parseString(
+ final String fromString) {
+ final DataVolumeModel model = new DataVolumeModel();
+ final String[] items = fromString.split(SEPARATOR); // $NON-NLS-1$
+ model.containerPath = items[0];
+ model.mountType = MountType.valueOf(items[1]);
+ switch (model.mountType) {
+ case CONTAINER:
+ model.setContainerMount(items[2]);
+ model.setSelected(Boolean.valueOf(items[3]));
+ break;
+ case HOST_FILE_SYSTEM:
+ // For Windows, there are multiple formats. If a user has specified
+ // a windows drive using the : separator, we have to form the
+ // host path by merging the path back together. If the user
+ // has specified an alternate format, we don't do this.
+ if (Platform.OS_WIN32.equals(Platform.getOS())
+ && items.length > 5) {
+ model.setHostPathMount(items[2] + SEPARATOR + items[3]);
+ model.setReadOnly(Boolean.valueOf(items[4]));
+ model.setSelected(Boolean.valueOf(items[5]));
+ } else {
+ model.setHostPathMount(items[2]);
+ model.setReadOnly(Boolean.valueOf(items[3]));
+ model.setSelected(Boolean.valueOf(items[4]));
+ }
+ break;
+ case NONE:
+ model.setSelected(Boolean.valueOf(items[2]));
+ break;
+ }
+ return model;
+ }
+
+ /**
+ * creates a {@link DataVolumeModel} from the 'volumeFrom' container info
+ *
+ * @param volumeFrom
+ * the value to parse.
+ *
+ * Format: <code>&lt;containerName&gt;</code>
+ *
+ * @See <a href="https://docs.docker.com/engine/userguide/dockervolumes/">
+ * https://docs.docker.com/engine/userguide/dockervolumes/</a>
+ */
+ public static DataVolumeModel parseVolumeFrom(String volumeFrom) {
+ final DataVolumeModel model = new DataVolumeModel();
+ model.mountType = MountType.CONTAINER;
+ model.containerMount = volumeFrom;
+ model.selected = true;
+ return model;
+ }
+
+ /**
+ * creates a {@link DataVolumeModel} from the 'volumeFrom' container info
+ *
+ * @param volumeFrom
+ * the value to parse. Format:
+ * <code>&lt;host_path&gt;:&lt;container_path&gt;:&lt;label_suffix_flag&gt;</code>
+ *
+ * @See <a href="https://docs.docker.com/engine/userguide/dockervolumes/">
+ * https://docs.docker.com/engine/userguide/dockervolumes/</a>
+ */
+ public static DataVolumeModel parseHostBinding(String volumeFrom) {
+ final DataVolumeModel model = new DataVolumeModel();
+ final String[] items = volumeFrom.split(SEPARATOR); // $NON-NLS-1$
+ // converts the host path to a valid Win32 path if Platform OS is Win32
+ model.setHostPathMount(convertToWin32Path(Platform.getOS(), items[0]));
+ model.containerPath = items[1];
+ model.mountType = MountType.HOST_FILE_SYSTEM;
+ if (items[2].equals("ro")) {
+ model.setReadOnly(true);
+ } else {
+ model.setReadOnly(false);
+ }
+ model.selected = true;
+ return model;
+ }
+
+ /**
+ * 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
+ * the path to convert
+ * @return the converted path or the given path
+ * @see {@link Platform#getOS()}
+ */
+ public static String convertToWin32Path(final String os,
+ final String path) {
+ if (os != null && os.equals(Platform.OS_WIN32)) {
+ // replace all "/" with "\" and then drive info (eg "/c/" to "C:/")
+ final Matcher m = Pattern.compile("^/([a-zA-Z])/").matcher(path); //$NON-NLS-1$
+ if (m.find()) {
+ final StringBuffer b = new StringBuffer();
+ m.appendReplacement(b, m.group(1).toUpperCase());
+ b.append(":\\"); //$NON-NLS-1$
+ m.appendTail(b);
+ return b.toString().replace('/', '\\'); // $NON-NLS-1$
+ // //$NON-NLS-2$
+ }
+ }
+ return path;
+ }
+
+ public String getContainerPath() {
+ return this.containerPath;
+ }
+
+ public void setContainerPath(final String containerPath) {
+ firePropertyChange(CONTAINER_PATH, this.containerPath,
+ this.containerPath = containerPath);
+ }
+
+ public String getMount() {
+ return mount;
+ }
+
+ public void setMount(final String mount) {
+ firePropertyChange(MOUNT, this.mount, this.mount = mount);
+ }
+
+ public MountType getMountType() {
+ return mountType;
+ }
+
+ public void setMountType(final MountType mountType) {
+ // ignore 'null' assignments that may come from the UpdateStrategy
+ // in
+ // the EditDataVolumePage when a radion button is unselected.
+ if (mountType == null) {
+ return;
+ }
+ firePropertyChange(MOUNT_TYPE, this.mountType,
+ this.mountType = mountType);
+ if (this.mountType == MountType.NONE) {
+ setMount("");
+ }
+
+ }
+
+ public String getHostPathMount() {
+ return hostPathMount;
+ }
+
+ public void setHostPathMount(final String hostPathMount) {
+ firePropertyChange(HOST_PATH_MOUNT, this.hostPathMount,
+ this.hostPathMount = hostPathMount);
+ if (this.mountType == MountType.HOST_FILE_SYSTEM) {
+ setMount(this.hostPathMount);
+ }
+ }
+
+ public boolean isReadOnly() {
+ return readOnly;
+ }
+
+ public void setReadOnly(final boolean readOnly) {
+ firePropertyChange(READ_ONLY_VOLUME, this.readOnly,
+ this.readOnly = readOnly);
+ }
+
+ public String getContainerMount() {
+ return this.containerMount;
+ }
+
+ public void setContainerMount(final String containerMount) {
+ firePropertyChange(CONTAINER_MOUNT, this.containerMount,
+ this.containerMount = containerMount);
+ if (this.mountType == MountType.CONTAINER) {
+ setMount(this.containerMount);
+ }
+ }
+
+ public boolean getSelected() {
+ return selected;
+ }
+
+ public void setSelected(final boolean selected) {
+ firePropertyChange(SELECTED, this.selected, this.selected = selected);
+ }
+
+ @Override
+ public int compareTo(final DataVolumeModel other) {
+ return this.getContainerPath().compareTo(other.getContainerPath());
+ }
+
+ // FIXME we should have a dedicated method to serialize the bean
+ @Override
+ public String toString() {
+ final StringBuffer buffer = new StringBuffer();
+ buffer.append(
+ this.containerPath + SEPARATOR + getMountType() + SEPARATOR);
+ switch (getMountType()) {
+ case CONTAINER:
+ buffer.append(getContainerMount());
+ break;
+ case HOST_FILE_SYSTEM:
+ buffer.append(getHostPathMount() + SEPARATOR); // $NON-NLS-1$
+ buffer.append(isReadOnly());
+ break;
+ case NONE:
+ break;
+ }
+ buffer.append(SEPARATOR).append(this.selected);
+ return buffer.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((id == null) ? 0 : id.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ DataVolumeModel other = (DataVolumeModel) obj;
+ if (id == null) {
+ if (other.id != null)
+ return false;
+ } else if (!id.equals(other.id))
+ return false;
+ return true;
+ }
+
+}
diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/LaunchShortcut.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/LaunchShortcut.java
index ed716c3361f..5ad81d2dcde 100644
--- a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/LaunchShortcut.java
+++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/LaunchShortcut.java
@@ -19,13 +19,18 @@ import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.IBinary;
import org.eclipse.cdt.core.model.ICProject;
+import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
import org.eclipse.cdt.debug.core.CDebugUtils;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.docker.launcher.DockerLaunchUIPlugin;
import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants;
import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin;
+import org.eclipse.cdt.managedbuilder.buildproperties.IOptionalBuildProperties;
+import org.eclipse.cdt.managedbuilder.core.IConfiguration;
+import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
import org.eclipse.cdt.ui.CElementLabelProvider;
+import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
@@ -253,6 +258,31 @@ public class LaunchShortcut implements ILaunchShortcut {
ILaunchConfiguration configuration = null;
ILaunchConfigurationType configType = getLaunchConfigType();
List<ILaunchConfiguration> candidateConfigs = Collections.emptyList();
+ IProject project = bin.getCProject().getProject();
+ ICConfigurationDescription cfgd = CoreModel.getDefault()
+ .getProjectDescription(project).getActiveConfiguration();
+ String connectionUri = null;
+ String imageName = null;
+ if (cfgd != null) {
+ IConfiguration cfg = ManagedBuildManager
+ .getConfigurationForDescription(cfgd);
+ if (cfg != null) {
+ IOptionalBuildProperties props = cfg
+ .getOptionalBuildProperties();
+ String containerBuild = props.getProperty(
+ ContainerCommandLauncher.CONTAINER_BUILD_ENABLED);
+ if (containerBuild != null) {
+ boolean containerBuildEnabled = Boolean
+ .parseBoolean(containerBuild);
+ if (containerBuildEnabled) {
+ connectionUri = props.getProperty(
+ ContainerCommandLauncher.CONNECTION_ID);
+ imageName = props
+ .getProperty(ContainerCommandLauncher.IMAGE_ID);
+ }
+ }
+ }
+ }
try {
ILaunchConfiguration[] configs = DebugPlugin.getDefault()
.getLaunchManager().getLaunchConfigurations(configType);
@@ -265,7 +295,17 @@ public class LaunchShortcut implements ILaunchShortcut {
if (projectName != null
&& projectName.equals(bin.getCProject()
.getProject().getName())) {
- candidateConfigs.add(config);
+ // if we have an active configuration with container
+ // build properties, make sure they match, otherwise
+ // add the launch config as a candidate
+ if (connectionUri.equals(config.getAttribute(
+ ILaunchConstants.ATTR_CONNECTION_URI,
+ connectionUri))) {
+ if (imageName.equals(config.getAttribute(
+ ILaunchConstants.ATTR_IMAGE, imageName))) {
+ candidateConfigs.add(config);
+ }
+ }
}
}
}
@@ -312,11 +352,40 @@ public class LaunchShortcut implements ILaunchShortcut {
String binaryPath = bin.getResource().getProjectRelativePath()
.toString();
+ IProject project = bin.getResource().getProject();
+
+ ICConfigurationDescription cfgd = CoreModel.getDefault()
+ .getProjectDescription(project).getActiveConfiguration();
+ IConfiguration cfg = ManagedBuildManager
+ .getConfigurationForDescription(cfgd);
+
+ IOptionalBuildProperties options = cfg.getOptionalBuildProperties();
+ boolean containerBuild = false;
+ String connectionId = null;
+ String imageName = null;
+
+ if (options != null) {
+ String containerBuildString = options.getProperty(
+ ContainerCommandLauncher.CONTAINER_BUILD_ENABLED);
+ if (containerBuildString != null) {
+ containerBuild = Boolean.parseBoolean(options.getProperty(
+ ContainerCommandLauncher.CONTAINER_BUILD_ENABLED));
+ }
+ if (containerBuild) {
+ connectionId = options.getProperty(
+ ContainerCommandLauncher.CONNECTION_ID);
+ imageName = options
+ .getProperty(ContainerCommandLauncher.IMAGE_ID);
+ }
+ }
+
ILaunchConfigurationType configType = getLaunchConfigType();
ILaunchConfigurationWorkingCopy wc = configType.newInstance(
null,
getLaunchManager().generateLaunchConfigurationName(
- bin.getElementName()));
+ bin.getResource().getName() + (imageName != null
+ ? ("[" + imageName + "]") //$NON-NLS-1$ //$NON-NLS-2$
+ : ""))); //$NON-NLS-1$
// DSF settings...use GdbUIPlugin preference store for defaults
IPreferenceStore preferenceStore = GdbUIPlugin.getDefault()
@@ -357,19 +426,25 @@ public class LaunchShortcut implements ILaunchShortcut {
Preferences prefs = InstanceScope.INSTANCE
.getNode(DockerLaunchUIPlugin.PLUGIN_ID);
- // get the connection from the ConnectionListener which waits for
- // any activity
- // from the DockerExplorerView
- IDockerConnection connection = ConnectionListener.getInstance()
+ // get the connection using following order:
+ // 1. connection used in build of project
+ // 2. current connection
+ // 3. first connection
+ IDockerConnection connection = null;
+ if (connectionId != null) {
+ connection = DockerConnectionManager.getInstance()
+ .getConnectionByUri(connectionId);
+ }
+ if (connection == null) {
+ connection = ConnectionListener.getInstance()
.getCurrentConnection();
+ }
if (connection == null) {
IDockerConnection[] connections = DockerConnectionManager
.getInstance().getConnections();
if (connections != null && connections.length > 0)
- connection = DockerConnectionManager.getInstance()
- .getConnections()[0];
+ connection = connections[0];
}
-
// issue error message if no connections exist
if (connection == null) {
Display.getDefault().syncExec(new Runnable() {
@@ -389,9 +464,14 @@ public class LaunchShortcut implements ILaunchShortcut {
wc.setAttribute(ILaunchConstants.ATTR_CONNECTION_URI,
connection.getUri());
- // get any default image if specified, otherwise use first
+ // use build image if one is specified, otherwise, see if a default
+ // image is set in preferences, otherwise find first image in image
+ // list
// image in image list for connection
- String image = prefs.get(PreferenceConstants.DEFAULT_IMAGE, null);
+ String image = imageName;
+ if (image == null) {
+ image = prefs.get(PreferenceConstants.DEFAULT_IMAGE, null);
+ }
if (image == null) {
List<IDockerImage> images = connection.getImages();
if (images != null && images.size() > 0)
diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/Messages.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/Messages.java
index f3d6003d8a1..dc90a4e3144 100644
--- a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/Messages.java
+++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/Messages.java
@@ -47,6 +47,13 @@ public class Messages extends NLS {
public static String ContainerTab_Warning_Connection_Not_Found;
public static String ContainerTab_Warning_Image_Not_Found;
+ public static String HeaderPreferencePage_Connection_Label;
+ public static String HeaderPreferencePage_Image_Label;
+ public static String HeaderPreferencePage_Remove_Label;
+ public static String HeaderPreferencePage_Remove_Tooltip;
+ public static String HeaderPreferencePage_Confirm_Removal_Title;
+ public static String HeaderPreferencePage_Confirm_Removal_Msg;
+
public static String Remote_GDB_Debugger_Options;
public static String Gdbserver_Settings_Tab_Name;
public static String Gdbserver_name_textfield_label;
@@ -90,6 +97,14 @@ public class Messages extends NLS {
public static String StandardGDBDebuggerPage14;
+ public static String ContainerPropertyTab_Title;
+ public static String ContainerPropertyTab_Enable_Msg;
+
+ public static String ContainerCommandLauncher_image_msg;
+ public static String CommandLauncher_CommandCancelled;
+
+ public static String ContainerCommandLauncher_invalid_values;
+
static {
// initialize resource bundle
NLS.initializeMessages(BUNDLE_NAME, Messages.class);
diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/SWTImagesFactory.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/SWTImagesFactory.java
index b886f2c6427..d08edf77d55 100644
--- a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/SWTImagesFactory.java
+++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/SWTImagesFactory.java
@@ -44,8 +44,21 @@ public class SWTImagesFactory {
private static final int NAME_PREFIX_LENGTH = NAME_PREFIX.length();
public static final String IMG_CONTAINER = NAME_PREFIX
+ "repository-middle.gif"; //$NON-NLS-1$
+ public static final String IMG_FOLDER_CLOSED = NAME_PREFIX
+ + "folder_closed.gif"; //$NON-NLS-1$
+ public static final String IMG_FILE = NAME_PREFIX + "file_obj.gif"; //$NON-NLS-1$
+ public static final String IMG_WARNING = NAME_PREFIX + "warning_obj.gif"; //$NON-NLS-1$
+ public static final String IMG_ERROR = NAME_PREFIX + "error_obj.gif"; //$NON-NLS-1$
+
public static final ImageDescriptor DESC_CONTAINER = createManaged("",
IMG_CONTAINER);
+ public static final ImageDescriptor DESC_FOLDER_CLOSED = createManaged("",
+ IMG_FOLDER_CLOSED);
+ public static final ImageDescriptor DESC_FILE = createManaged("", IMG_FILE);
+ public static final ImageDescriptor DESC_WARNING = createManaged("",
+ IMG_WARNING);
+ public static final ImageDescriptor DESC_ERROR = createManaged("",
+ IMG_ERROR);
private static ImageDescriptor createManaged(String prefix, String name) {
return createManaged(imageRegistry, prefix, name);
diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/messages.properties b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/messages.properties
index 15e1b19a0c7..06609d611cf 100644
--- a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/messages.properties
+++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/messages.properties
@@ -40,6 +40,21 @@ ContainerTab_Error_No_Images=No Docker Images exist
ContainerTab_Warning_Connection_Not_Found=Docker Connection: {0} for Launch Configuration not found: defaulting to {1}
ContainerTab_Warning_Image_Not_Found=Docker Image: {0} is not a valid pulled image in current Connection: {1}
+ContainerPropertyTab_Title=Container Settings
+ContainerPropertyTab_Enable_Msg=Build inside Docker Image
+
+HeaderPreferencePage_Connection_Label=Connection
+HeaderPreferencePage_Image_Label=Image
+HeaderPreferencePage_Remove_Label=Remove
+HeaderPreferencePage_Remove_Tooltip=Remove headers cached from Docker image
+HeaderPreferencePage_Confirm_Removal_Title=Confirm Header File Removal
+HeaderPreferencePage_Confirm_Removal_Msg=Confirm removal of specified cached header files
+
+ContainerCommandLauncher_image_msg=[Running in image <{0}>]
+ContainerCommandLauncher_invalid_values=Invalid values for Connection and/or Image name
+
+CommandLauncher_CommandCancelled=Command cancelled
+
Remote_GDB_Debugger_Options=Docker Container GDB Debugger Options
Gdbserver_Settings_Tab_Name=Gdbserver Settings
Gdbserver_name_textfield_label=Gdbserver path:
diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ui/preferences/DockerHeaderPreferencePage.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ui/preferences/DockerHeaderPreferencePage.java
new file mode 100644
index 00000000000..0e0269ab90d
--- /dev/null
+++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ui/preferences/DockerHeaderPreferencePage.java
@@ -0,0 +1,356 @@
+package org.eclipse.cdt.internal.docker.launcher.ui.preferences;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.cdt.docker.launcher.DockerLaunchUIPlugin;
+import org.eclipse.cdt.internal.docker.launcher.Messages;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.preference.PreferencePage;
+import org.eclipse.jface.viewers.ColumnWeightData;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.TableLayout;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+public class DockerHeaderPreferencePage extends PreferencePage
+ implements IWorkbenchPreferencePage, Listener {
+
+ // SWT Widgets and content providers
+ private Table hdrTable;
+ private TableViewer hdrTableViewer;
+ private HeaderContentProvider provider;
+ private Button removeButton;
+ private List<IPath> directories;
+
+ private final class HeaderContentProvider
+ implements IStructuredContentProvider, ITableLabelProvider {
+
+ /**
+ * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(Object)
+ */
+ @Override
+ public Object[] getElements(Object inputElement) {
+ return directories.toArray();
+ }
+
+ /**
+ * @see org.eclipse.jface.viewers.IContentProvider#dispose()
+ */
+ @Override
+ public void dispose() {
+ }
+
+ /**
+ * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(Viewer,
+ * Object, Object)
+ */
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput,
+ Object newInput) {
+ }
+
+ /**
+ * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(Object,
+ * int)
+ */
+ @Override
+ public Image getColumnImage(Object element, int columnIndex) {
+ return null;
+ }
+
+ private String readNameFile(IPath path) {
+ // try and read real name from special .name file found in
+ // directory.
+ IPath namePath = path.append(".name"); //$NON-NLS-1$
+ // default to use last directory segment if any problems occur.
+ String name = path.lastSegment();
+ if (namePath.toFile().exists()) {
+ try (FileReader reader = new FileReader(namePath.toFile());
+ BufferedReader bufferReader = new BufferedReader(
+ reader);) {
+ name = bufferReader.readLine();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ return name;
+ }
+
+ /**
+ * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(Object,
+ * int)
+ */
+ @Override
+ public String getColumnText(Object element, int columnIndex) {
+ IPath path = (IPath) element;
+ if (columnIndex == 0) {
+ IPath connectionPath = path.removeLastSegments(1);
+ String connectionName = readNameFile(connectionPath);
+ return connectionName;
+ }
+ String imageName = readNameFile(path);
+ return imageName;
+ }
+
+ /**
+ * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(ILabelProviderListener)
+ */
+ @Override
+ public void addListener(ILabelProviderListener listener) {
+ }
+
+ /**
+ * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(Object,
+ * String)
+ */
+ @Override
+ public boolean isLabelProperty(Object element, String property) {
+ return false;
+ }
+
+ /**
+ * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(ILabelProviderListener)
+ */
+ @Override
+ public void removeListener(ILabelProviderListener listener) {
+ }
+
+ }
+
+ public DockerHeaderPreferencePage() {
+ noDefaultAndApplyButton();
+ provider = new HeaderContentProvider();
+ }
+
+ @Override
+ public void init(IWorkbench workbench) {
+ directories = new ArrayList<>();
+ IPath pluginPath = Platform
+ .getStateLocation(
+ Platform.getBundle(DockerLaunchUIPlugin.PLUGIN_ID))
+ .append("HEADERS"); //$NON-NLS-1$
+ File d = pluginPath.toFile();
+
+ if (d.exists() && d.isDirectory()) {
+ File[] connections = d.listFiles();
+ for (File connection : connections) {
+ if (connection.isDirectory()) {
+ File[] images = connection.listFiles();
+ for (File image : images) {
+ if (image.isDirectory()) {
+ directories
+ .add(pluginPath.append(connection.getName())
+ .append(image.getName()));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ protected Control createContents(Composite parent) {
+ Composite page = createComposite(parent, 1, 2, false, null, -1, -1,
+ GridData.FILL);
+ GridData gd = (GridData) page.getLayoutData();
+ gd.grabExcessHorizontalSpace = true;
+ gd.grabExcessVerticalSpace = true;
+
+ // SystemWidgetHelpers.createLabel(page,
+ // SystemResources.RESID_PREF_SIGNON_DESCRIPTION, 2);
+
+ // Header table
+ hdrTable = new Table(page, SWT.FULL_SELECTION | SWT.MULTI | SWT.V_SCROLL
+ | SWT.H_SCROLL | SWT.BORDER);
+ hdrTable.setLinesVisible(true);
+ hdrTable.setHeaderVisible(true);
+ hdrTable.addListener(SWT.Selection, this);
+
+ TableLayout tableLayout = new TableLayout();
+ tableLayout.addColumnData(new ColumnWeightData(60, true));
+ tableLayout.addColumnData(new ColumnWeightData(40, true));
+ hdrTable.setLayout(tableLayout);
+
+ gd = new GridData(GridData.FILL_BOTH);
+ gd.grabExcessHorizontalSpace = true;
+ gd.grabExcessVerticalSpace = true;
+
+ hdrTable.setLayoutData(gd);
+
+ // Connection column
+ TableColumn connectionColumn = new TableColumn(hdrTable, SWT.NONE);
+ connectionColumn
+ .setText(Messages.HeaderPreferencePage_Connection_Label);
+
+ // Image column
+ TableColumn imageColumn = new TableColumn(hdrTable, SWT.NONE);
+ imageColumn.setText(Messages.HeaderPreferencePage_Image_Label);
+
+ hdrTableViewer = new TableViewer(hdrTable);
+ hdrTableViewer.setContentProvider(provider);
+ hdrTableViewer.setLabelProvider(provider);
+ hdrTableViewer.setInput(directories);
+
+ // Create the Button bar for add, change and remove
+ Composite buttonBar = createComposite(page, 1, 1, false, null, -1, -1,
+ GridData.FILL);
+ gd = (GridData) buttonBar.getLayoutData();
+ gd.grabExcessHorizontalSpace = false;
+ gd.grabExcessVerticalSpace = true;
+
+ removeButton = createPushButton(buttonBar, this,
+ Messages.HeaderPreferencePage_Remove_Label,
+ Messages.HeaderPreferencePage_Remove_Tooltip);
+
+ removeButton.setEnabled(false);
+ return parent;
+ }
+
+ private static Composite createComposite(Composite parent, int parentSpan,
+ int numColumns, boolean border, String label, int marginSize,
+ int spacingSize, int verticalAlignment) {
+ // border = true;
+ boolean borderNeeded = border;
+ if (label != null)
+ borderNeeded = true; // force the case
+ int style = SWT.NULL;
+ if (borderNeeded)
+ style |= SWT.SHADOW_ETCHED_IN;
+ Composite composite = null;
+ if (borderNeeded) {
+ composite = new Group(parent, style);
+ if (label != null)
+ ((Group) composite).setText(label);
+ } else {
+ composite = new Composite(parent, style);
+ }
+ // GridLayout
+ GridLayout layout = new GridLayout();
+ layout.numColumns = numColumns;
+ if (marginSize != -1) {
+ layout.marginWidth = 0;
+ layout.marginHeight = 0;
+ }
+ if (spacingSize != -1) {
+ layout.horizontalSpacing = 0;
+ layout.verticalSpacing = 0;
+ }
+ composite.setLayout(layout);
+ // GridData
+ GridData data = new GridData();
+ data.horizontalSpan = parentSpan;
+ data.horizontalAlignment = GridData.FILL;
+ data.grabExcessHorizontalSpace = true;
+
+ data.verticalAlignment = verticalAlignment;
+ data.grabExcessVerticalSpace = false;
+
+ composite.setLayoutData(data);
+ return composite;
+ }
+
+ public static Button createPushButton(Composite group, Listener listener,
+ String label, String tooltip) {
+ Button button = new Button(group, SWT.PUSH);
+ button.setText(label);
+ if (listener != null)
+ button.addListener(SWT.Selection, listener);
+ GridData data = new GridData();
+ data.horizontalAlignment = GridData.FILL;
+ data.grabExcessHorizontalSpace = true;
+ button.setLayoutData(data);
+ if (tooltip != null)
+ button.setToolTipText(tooltip);
+ return button;
+ }
+
+ private class DialogStatus {
+ private boolean status;
+
+ public DialogStatus(boolean status) {
+ this.status = status;
+ }
+
+ public void setStatus(boolean status) {
+ this.status = status;
+ }
+
+ public boolean getStatus() {
+ return status;
+ }
+ }
+
+ /**
+ * @see org.eclipse.swt.widgets.Listener#handleEvent(Event)
+ */
+ @Override
+ public void handleEvent(Event event) {
+ if (event.type == SWT.Selection) {
+ if (event.widget == removeButton) {
+ final DialogStatus confirmed = new DialogStatus(false);
+ Display.getDefault().syncExec(() -> {
+ boolean status = MessageDialog.openConfirm(getShell(),
+ Messages.HeaderPreferencePage_Confirm_Removal_Title,
+ Messages.HeaderPreferencePage_Confirm_Removal_Msg);
+ confirmed.setStatus(status);
+ });
+ if (!confirmed.getStatus()) {
+ return;
+ }
+ int[] indicies = hdrTable.getSelectionIndices();
+ for (int idx = indicies.length - 1; idx >= 0; idx--) {
+ IPath dirPath = directories.get(idx);
+ File f = dirPath.toFile();
+ if (f.exists() && f.isDirectory()) {
+ recursiveDelete(f);
+ }
+ directories.remove(idx);
+ }
+
+ hdrTableViewer.refresh();
+ }
+
+ // Update table buttons based on changes
+ if (hdrTable.getSelectionCount() > 0) {
+ removeButton.setEnabled(true);
+ } else {
+ removeButton.setEnabled(false);
+ }
+ }
+ }
+
+ private void recursiveDelete(File dir) {
+ File[] contents = dir.listFiles();
+ if (contents != null) {
+ for (File f : contents) {
+ recursiveDelete(f);
+ }
+ }
+ dir.delete();
+ }
+}
+

Back to the top