1 /*******************************************************************************
2  * Copyright (c) 2007, 2018 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Axel Richard (Obeo) - Bug 41353 - Launch configurations prototypes
14  *******************************************************************************/
15 package org.eclipse.debug.internal.ui.importexport.launchconfigurations;
16 
17 import java.io.File;
18 import java.text.MessageFormat;
19 import java.util.ArrayList;
20 import java.util.List;
21 
22 import org.eclipse.core.runtime.CoreException;
23 import org.eclipse.core.runtime.IPath;
24 import org.eclipse.core.runtime.IProgressMonitor;
25 import org.eclipse.core.runtime.IStatus;
26 import org.eclipse.core.runtime.Path;
27 import org.eclipse.core.runtime.Status;
28 import org.eclipse.core.runtime.jobs.Job;
29 import org.eclipse.debug.core.DebugPlugin;
30 import org.eclipse.debug.core.ILaunchConfiguration;
31 import org.eclipse.debug.internal.core.IInternalDebugCoreConstants;
32 import org.eclipse.debug.internal.core.LaunchManager;
33 import org.eclipse.debug.internal.ui.DebugUIPlugin;
34 import org.eclipse.debug.internal.ui.IDebugHelpContextIds;
35 import org.eclipse.debug.internal.ui.IInternalDebugUIConstants;
36 import org.eclipse.debug.internal.ui.SWTFactory;
37 import org.eclipse.debug.ui.DebugUITools;
38 import org.eclipse.jface.dialogs.IDialogSettings;
39 import org.eclipse.jface.dialogs.MessageDialog;
40 import org.eclipse.jface.viewers.ITreeContentProvider;
41 import org.eclipse.jface.viewers.StructuredSelection;
42 import org.eclipse.swt.SWT;
43 import org.eclipse.swt.custom.BusyIndicator;
44 import org.eclipse.swt.events.SelectionAdapter;
45 import org.eclipse.swt.events.SelectionEvent;
46 import org.eclipse.swt.graphics.Image;
47 import org.eclipse.swt.layout.GridData;
48 import org.eclipse.swt.widgets.Button;
49 import org.eclipse.swt.widgets.Composite;
50 import org.eclipse.swt.widgets.DirectoryDialog;
51 import org.eclipse.swt.widgets.Text;
52 import org.eclipse.ui.PlatformUI;
53 import org.eclipse.ui.dialogs.FileSystemElement;
54 import org.eclipse.ui.dialogs.WizardResourceImportPage;
55 import org.eclipse.ui.model.AdaptableList;
56 import org.eclipse.ui.model.WorkbenchContentProvider;
57 import org.eclipse.ui.wizards.datatransfer.FileSystemStructureProvider;
58 
59 /**
60  * This class providers the one and only page for the import launch configurations wizard
61  * @since 3.4.0
62  */
63 public class ImportLaunchConfigurationsWizardPage extends WizardResourceImportPage {
64 
65 	/**
66 	 * Represents a debug view of the file system, in that we only care about folders and files with the
67 	 * extension *.launch
68 	 * @since 3.4.0
69 	 */
70 	class DebugFileSystemElement extends FileSystemElement {
71 
72 		private boolean populated = false;
73 
DebugFileSystemElement(String name, FileSystemElement parent, boolean isDirectory)74 		public DebugFileSystemElement(String name, FileSystemElement parent, boolean isDirectory) {
75 			super(name, parent, isDirectory);
76 		}
77 
setPopulated()78 		public void setPopulated() {
79 			populated = true;
80 		}
81 
isPopulated()82 		public boolean isPopulated() {
83 			return populated;
84 		}
85 
86 		@Override
getFiles()87 		public AdaptableList getFiles() {
88 			if(!populated) {
89 				populateElementChildren();
90 			}
91 			return super.getFiles();
92 		}
93 
94 		@Override
getFolders()95 		public AdaptableList getFolders() {
96 			if(!populated) {
97 				populateElementChildren();
98 			}
99 			return super.getFolders();
100 		}
101 
102 		/**
103 		 * Populates the children of the specified parent <code>FileSystemElement</code>
104 		 * @param element
105 		 * @param folderonly
106 		 */
populateElementChildren()107 		private void populateElementChildren() {
108 			FileSystemStructureProvider provider = FileSystemStructureProvider.INSTANCE;
109 			List<File> allchildren = provider.getChildren(this.getFileSystemObject());
110 			DebugFileSystemElement newelement = null;
111 			for (File child : allchildren) {
112 				if(child.isFile()) {
113 					Path childpath = new Path(child.getAbsolutePath());
114 					String extension = childpath.getFileExtension();
115 					if (extension != null && (extension.equals(ILaunchConfiguration.LAUNCH_CONFIGURATION_FILE_EXTENSION) || extension.equals(ILaunchConfiguration.LAUNCH_CONFIGURATION_PROTOTYPE_FILE_EXTENSION))) {
116 						newelement = new DebugFileSystemElement(provider.getLabel(child), this, provider.isFolder(child));
117 						newelement.setFileSystemObject(child);
118 					}
119 				}
120 				else {
121 					newelement = new DebugFileSystemElement(provider.getLabel(child), this, provider.isFolder(child));
122 					newelement.setFileSystemObject(child);
123 				}
124 			}
125 			setPopulated();
126 		}
127 	}
128 
129 	private String OVERWRITE = "import_config_overwrite"; //$NON-NLS-1$
130 	private String OLD_PATH = "import_config_oldpath"; //$NON-NLS-1$
131 
132 	private Text fFromDirectory = null;
133 	private Button fOverwrite = null;
134 
135 	/**
136 	 * Constructor
137 	 */
ImportLaunchConfigurationsWizardPage()138 	public ImportLaunchConfigurationsWizardPage() {
139 		super(WizardMessages.ImportLaunchConfigurationsWizardPage_0, new StructuredSelection());
140 		setTitle(WizardMessages.ImportLaunchConfigurationsWizardPage_0);
141 		setMessage(WizardMessages.ImportLaunchConfigurationsWizardPage_5);
142 	}
143 
144 	@Override
createControl(Composite parent)145 	public void createControl(Composite parent) {
146 		Composite comp = SWTFactory.createComposite(parent, 1, 1, GridData.FILL_BOTH);
147 		createRootDirectoryGroup(comp);
148 		createFileSelectionGroup(comp);
149 		IDialogSettings settings = getDialogSettings();
150 		fOverwrite = SWTFactory.createCheckButton(comp, WizardMessages.ImportLaunchConfigurationsWizardPage_1, null, settings.getBoolean(OVERWRITE), 1);
151 		String oldpath = settings.get(OLD_PATH);
152 		oldpath = (oldpath == null ? IInternalDebugCoreConstants.EMPTY_STRING : oldpath);
153 		fFromDirectory.setText((oldpath == null ? IInternalDebugCoreConstants.EMPTY_STRING : oldpath));
154 		resetSelection(new Path(oldpath));
155 		setControl(comp);
156 		PlatformUI.getWorkbench().getHelpSystem().setHelp(comp, IDebugHelpContextIds.IMPORT_LAUNCH_CONFIGURATIONS_PAGE);
157 		setPageComplete(false);
158 		//do not set page complete, Eclipse UI guidelines states wizards cannot start off with an error showing
159 	}
160 
161 	/**
162 	 * Performs the actual work of the wizard page and return is the work was successful
163 	 * @return true if the import completed normally, false otherwise
164 	 */
finish()165 	public boolean finish() {
166 		IDialogSettings settings = getDialogSettings();
167 		settings.put(OVERWRITE, fOverwrite.getSelection());
168 		settings.put(OLD_PATH, fFromDirectory.getText().trim());
169 		boolean overwrite = fOverwrite.getSelection();
170 		File config, newconfig = null;
171 		boolean owall = false, nowall = false;
172 		MessageDialog dialog = null;
173 		final List<File> filesToImport = new ArrayList<>();
174 		for (Object resource :  getSelectedResources()) {
175 			config = (File) ((DebugFileSystemElement) resource).getFileSystemObject();
176 			newconfig = new File(new Path(LaunchManager.LOCAL_LAUNCH_CONFIGURATION_CONTAINER_PATH.toOSString()).append(config.getName()).toOSString());
177 			if(newconfig.exists() & !overwrite) {
178 				if(nowall) {
179 					continue;
180 				}
181 				if(!owall) {
182 					dialog = new MessageDialog(DebugUIPlugin.getShell(),
183 							WizardMessages.ExportLaunchConfigurationsWizardPage_11,
184 							null,
185  MessageFormat.format(WizardMessages.ExportLaunchConfigurationsWizardPage_12, new Object[] { config.getName() }),
186 							MessageDialog.QUESTION, new String[] {WizardMessages.ExportLaunchConfigurationsWizardPage_13, WizardMessages.ExportLaunchConfigurationsWizardPage_14, WizardMessages.ExportLaunchConfigurationsWizardPage_15, WizardMessages.ExportLaunchConfigurationsWizardPage_16, WizardMessages.ExportLaunchConfigurationsWizardPage_17}, 0);
187 					int ret = dialog.open();
188 					switch(ret) {
189 						case 0: {
190 							filesToImport.add(config);
191 							break;
192 						}
193 						case 1: {
194 							owall = true;
195 							filesToImport.add(config);
196 							break;
197 						}
198 						case 3: {
199 							nowall = true;
200 							break;
201 						}
202 						case 4: {
203 							return true;
204 						}
205 						default:
206 							break;
207 					}
208 				} else if(!nowall) {
209 					filesToImport.add(config);
210 				}
211 			} else {
212 				filesToImport.add(config);
213 			}
214 		}
215 
216 		if (!filesToImport.isEmpty()) {
217 			Job job = new Job(WizardMessages.ExportLaunchConfigurationsWizard_0) {
218 				@Override
219 				public IStatus run(IProgressMonitor monitor) {
220 					LaunchManager launchManager = (LaunchManager) DebugPlugin.getDefault().getLaunchManager();
221 					try {
222 						launchManager.importConfigurations(filesToImport.toArray(new File[filesToImport.size()]), monitor);
223 					} catch (CoreException e) {
224 						return e.getStatus();
225 					}
226 					return Status.OK_STATUS;
227 				}
228 			};
229 			job.schedule();
230 		}
231 		return true;
232 	}
233 
234 	@Override
getImage()235 	public Image getImage() {
236 		return DebugUITools.getImage(IInternalDebugUIConstants.IMG_WIZBAN_IMPORT_CONFIGS);
237 	}
238 
239 	@Override
updateWidgetEnablements()240 	protected void updateWidgetEnablements() {
241 		setPageComplete(determinePageCompletion());
242 	}
243 
244 	@Override
determinePageCompletion()245 	protected boolean determinePageCompletion() {
246 		if(fFromDirectory.getText().trim().equals(IInternalDebugCoreConstants.EMPTY_STRING)) {
247 			setErrorMessage(WizardMessages.ImportLaunchConfigurationsWizardPage_3);
248 			return false;
249 		}
250 		if (getSelectedResources().size() < 1) {
251 			setErrorMessage(WizardMessages.ImportLaunchConfigurationsWizardPage_4);
252 			return false;
253 		}
254 		setErrorMessage(null);
255 		setMessage(WizardMessages.ImportLaunchConfigurationsWizardPage_5);
256 		return true;
257 	}
258 
259 	@Override
createSourceGroup(Composite parent)260 	protected void createSourceGroup(Composite parent) {}
261 
262 	/**
263 	 *	Create the group for creating the root directory
264 	 */
createRootDirectoryGroup(Composite parent)265 	protected void createRootDirectoryGroup(Composite parent) {
266 		Composite comp = SWTFactory.createComposite(parent, parent.getFont(), 3, 1, GridData.FILL_HORIZONTAL, 0, 0);
267 		SWTFactory.createLabel(comp, WizardMessages.ImportLaunchConfigurationsWizardPage_6, 1);
268 		// source name entry field
269 		fFromDirectory = SWTFactory.createText(comp, SWT.BORDER | SWT.SINGLE | SWT.READ_ONLY, 1, GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL);
270 		// source browse button
271 		Button browse = SWTFactory.createPushButton(comp, WizardMessages.ImportLaunchConfigurationsWizardPage_7, null);
272 		browse.addSelectionListener(new SelectionAdapter () {
273 			@Override
274 			public void widgetSelected(SelectionEvent e) {
275 				DirectoryDialog dd = new DirectoryDialog(getContainer().getShell(), SWT.SHEET);
276 				dd.setText(WizardMessages.ImportLaunchConfigurationsWizardPage_0);
277 				String filename = dd.open();
278 				if(filename != null) {
279 					IPath path = new Path(filename);
280 					if (path != null) {
281 						fFromDirectory.setText(path.toString());
282 						resetSelection(path);
283 						setPageComplete(determinePageCompletion());
284 					}
285 				}
286 			}
287 		});
288 	}
289 
290 	/**
291 	 * Resets the selection of the tree root element for the viewer
292 	 * @param path the path from the text widget
293 	 */
resetSelection(final IPath path)294 	protected void resetSelection(final IPath path) {
295 		BusyIndicator.showWhile(getShell().getDisplay(), () -> {
296 			File file = new File(path.toOSString());
297 			DebugFileSystemElement dummyparent = new DebugFileSystemElement(IInternalDebugCoreConstants.EMPTY_STRING,
298 					null, true);
299 			dummyparent.setPopulated();
300 			DebugFileSystemElement element = new DebugFileSystemElement(
301 					FileSystemStructureProvider.INSTANCE.getLabel(file), dummyparent, file.isDirectory());
302 			element.setFileSystemObject(file);
303 			element.getFiles();
304 			selectionGroup.setRoot(dummyparent);
305 		});
306 	}
307 
308 	@Override
getFileProvider()309 	protected ITreeContentProvider getFileProvider() {
310 		return new WorkbenchContentProvider() {
311 			@Override
312 			public Object[] getChildren(Object o) {
313 				if (o instanceof DebugFileSystemElement) {
314 					DebugFileSystemElement element = (DebugFileSystemElement) o;
315 					return element.getFiles().getChildren(element);
316 				}
317 				return new Object[0];
318 			}
319 		};
320 	}
321 
322 	@Override
323 	protected ITreeContentProvider getFolderProvider() {
324 		return new WorkbenchContentProvider() {
325 			@Override
326 			public Object[] getChildren(Object o) {
327 				if (o instanceof DebugFileSystemElement) {
328 					DebugFileSystemElement element = (DebugFileSystemElement) o;
329 					return element.getFolders().getChildren();
330 				}
331 				return new Object[0];
332 			}
333 
334 			@Override
335 			public boolean hasChildren(Object o) {
336 				if (o instanceof DebugFileSystemElement) {
337 					DebugFileSystemElement element = (DebugFileSystemElement) o;
338 					if (element.isPopulated()) {
339 						return getChildren(element).length > 0;
340 					}
341 					//If we have not populated then wait until asked
342 					return true;
343 				}
344 				return false;
345 			}
346 		};
347 	}
348 
349 }
350