1 /*******************************************************************************
2  * Copyright (c) 2000, 2020 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  *     Paul Pazderski - Bug 251642: show process termination time
14  *******************************************************************************/
15 package org.eclipse.debug.internal.ui.preferences;
16 
17 
18 import java.text.DateFormat;
19 import java.text.ParseException;
20 import java.util.Arrays;
21 import java.util.Date;
22 import java.util.regex.Matcher;
23 import java.util.regex.Pattern;
24 
25 import org.eclipse.debug.core.DebugPlugin;
26 import org.eclipse.debug.core.model.IDebugElement;
27 import org.eclipse.debug.core.model.IProcess;
28 import org.eclipse.debug.internal.ui.IDebugHelpContextIds;
29 import org.eclipse.debug.internal.ui.SWTFactory;
30 import org.eclipse.jface.resource.JFaceResources;
31 import org.eclipse.swt.SWT;
32 import org.eclipse.swt.custom.BidiSegmentEvent;
33 import org.eclipse.swt.custom.BidiSegmentListener;
34 import org.eclipse.swt.custom.StyledText;
35 import org.eclipse.swt.graphics.Font;
36 import org.eclipse.swt.layout.GridData;
37 import org.eclipse.swt.widgets.Composite;
38 import org.eclipse.swt.widgets.Control;
39 import org.eclipse.swt.widgets.Text;
40 import org.eclipse.ui.PlatformUI;
41 import org.eclipse.ui.dialogs.PropertyPage;
42 
43 public class ProcessPropertyPage extends PropertyPage {
44 
45 	private static Font fHeadingFont = JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT);
46 
47 	/**
48 	 * Constructor for ProcessPropertyPage
49 	 */
ProcessPropertyPage()50 	public ProcessPropertyPage() {
51 		super();
52 	}
53 
54 	@Override
createContents(Composite ancestor)55 	protected Control createContents(Composite ancestor) {
56 		noDefaultAndApplyButton();
57 		Composite parent = SWTFactory.createComposite(ancestor, ancestor.getFont(), 1, 1, GridData.FILL_BOTH);
58 
59 		IProcess proc = getProcess();
60 
61 		// create the process launch time section
62 		SWTFactory.createLabel(parent, DebugPreferencesMessages.ProcessPropertyPage_0, fHeadingFont, 1);
63 		Text text = SWTFactory.createText(parent, SWT.READ_ONLY, 1);
64 		((GridData)text.getLayoutData()).horizontalIndent = 10;
65 		PlatformUI.getWorkbench().getHelpSystem().setHelp(text, IDebugHelpContextIds.PROCESS_PAGE_RUN_AT);
66 		text.setText(getLaunchTimeText(proc));
67 		text.setBackground(parent.getBackground());
68 		SWTFactory.createVerticalSpacer(parent, 2);
69 
70 		// create the process terminate time section
71 		SWTFactory.createLabel(parent, DebugPreferencesMessages.ProcessPropertyPage_10, fHeadingFont, 1);
72 		text = SWTFactory.createText(parent, SWT.READ_ONLY, 1);
73 		((GridData) text.getLayoutData()).horizontalIndent = 10;
74 		PlatformUI.getWorkbench().getHelpSystem().setHelp(text, IDebugHelpContextIds.PROCESS_PAGE_TERMINATE_AT);
75 		text.setText(getTerminateTimeText(proc));
76 		text.setBackground(parent.getBackground());
77 		SWTFactory.createVerticalSpacer(parent, 2);
78 
79 	//create the path name section
80 		SWTFactory.createLabel(parent, DebugPreferencesMessages.ProcessPropertyPage_1, fHeadingFont, 1);
81 		text = SWTFactory.createText(parent, SWT.WRAP | SWT.READ_ONLY, 1);
82 		((GridData)text.getLayoutData()).horizontalIndent = 10;
83 		text.setText(getPathText(proc));
84 		text.setBackground(parent.getBackground());
85 		SWTFactory.createVerticalSpacer(parent, 2);
86 
87 	//create working directory section
88 		SWTFactory.createLabel(parent, DebugPreferencesMessages.ProcessPropertyPage_6, fHeadingFont, 1);
89 		text = SWTFactory.createText(parent, SWT.WRAP | SWT.READ_ONLY, 1);
90 		((GridData)text.getLayoutData()).horizontalIndent = 10;
91 		text.setText(getWorkingDirectory(proc));
92 		text.setBackground(parent.getBackground());
93 		SWTFactory.createVerticalSpacer(parent, 2);
94 
95 	//create command line section
96 		SWTFactory.createLabel(parent, DebugPreferencesMessages.ProcessPropertyPage_Command_Line__1, fHeadingFont, 1);
97 		StyledText styledText = SWTFactory.createStyledText(parent,
98 				SWT.WRAP | SWT.READ_ONLY | SWT.BORDER | SWT.V_SCROLL,
99 				1,
100 				convertWidthInCharsToPixels(13),
101 				convertHeightInCharsToPixels(10),
102 				GridData.FILL_BOTH);
103 		styledText.setBackground(parent.getBackground());
104 		((GridData)styledText.getLayoutData()).horizontalIndent = 10;
105 		String commandLineText = DebugPreferencesMessages.ProcessPropertyPage_5;
106 		if (proc != null) {
107 			commandLineText = proc.getAttribute(IProcess.ATTR_CMDLINE);
108 			String[] arguments = DebugPlugin.parseArguments(commandLineText);
109 			int count = arguments.length;
110 			if (count > 1) {
111 				// render as one argument per line, but don't copy line delimiters to clipboard:
112 				final int[] segments = new int[count - 1];
113 				commandLineText = DebugPlugin.renderArguments(arguments, segments);
114 
115 				styledText.addBidiSegmentListener(new BidiSegmentListener() {
116 					@Override
117 					public void lineGetSegments(BidiSegmentEvent event) {
118 						int offset = event.lineOffset;
119 						int end = offset + event.lineText.length();
120 
121 						// extract segments for the current line:
122 						int iStart = Arrays.binarySearch(segments, offset);
123 						if (iStart < 0) {
124 							iStart = -iStart - 1;
125 						}
126 						int i = iStart;
127 						while (i < segments.length && segments[i] < end) {
128 							i++;
129 						}
130 						int n = i - iStart;
131 						if (n > 0) {
132 							if (n == segments.length) {
133 								event.segments = segments;
134 							} else {
135 								int[] lineSegments = new int[n];
136 								System.arraycopy(segments, iStart, lineSegments, 0, n);
137 								event.segments = lineSegments;
138 							}
139 							final char[] chars = new char[n];
140 							Arrays.fill(chars, '\n');
141 							event.segmentsChars = chars;
142 						}
143 					}
144 				});
145 			}
146 		}
147 		if(commandLineText != null) {
148 			styledText.setText(commandLineText);
149 		}
150 
151 	//create environment section
152 		SWTFactory.createLabel(parent, DebugPreferencesMessages.ProcessPropertyPage_7, fHeadingFont, 1);
153 		text = SWTFactory.createText(parent,
154 				SWT.H_SCROLL | SWT.READ_ONLY | SWT.BORDER | SWT.V_SCROLL,
155 				1,
156 				convertWidthInCharsToPixels(13),
157 				convertHeightInCharsToPixels(8),
158 				GridData.FILL_BOTH);
159 		text.setBackground(parent.getBackground());
160 		((GridData)text.getLayoutData()).horizontalIndent = 10;
161 		text.setText(getEnvironment(proc));
162 
163 		setTitle(DebugPreferencesMessages.ProcessPropertyPage_2);
164 		return parent;
165 	}
166 
167 	/**
168 	 * Gets the process from the selected element
169 	 * @return the process or null if the element is not a process
170 	 *
171 	 * @since 3.2
172 	 */
getProcess()173 	private IProcess getProcess() {
174 		IProcess proc = null;
175 		Object obj = getElement();
176 		if (obj instanceof IDebugElement) {
177 			obj = ((IDebugElement)obj).getDebugTarget().getProcess();
178 		}
179 		if (obj instanceof IProcess) {
180 			proc = ((IProcess)obj);
181 		}
182 		return proc;
183 	}
184 
185 	/**
186 	 * returns the path text
187 	 * @param proc the process to extract the path text from
188 	 * @return the path text or a message indicating no path text available
189 	 *
190 	 * @see DebugPlugin#ATTR_PATH
191 	 * @since 3.2
192 	 */
getPathText(IProcess proc)193 	private String getPathText(IProcess proc) {
194 		String text = DebugPreferencesMessages.ProcessPropertyPage_3;
195 		if(proc != null) {
196 			String tmp = proc.getAttribute(DebugPlugin.ATTR_PATH);
197 			if(tmp != null) {
198 				return tmp;
199 			}
200 			tmp = proc.getLabel();
201 			// TODO remove this ugly workaround after removing start time from process label
202 			// in jdt
203 			int idx = tmp.lastIndexOf('(');
204 			if(idx < 0) {
205 				idx = tmp.length();
206 			}
207 			text = tmp.substring(0, idx);
208 		}
209 		return text;
210 	}
211 
212 	/**
213 	 * Try to get the launch time for the process.
214 	 *
215 	 * @param proc the process to get launch time for
216 	 * @return the launch time or default replacement
217 	 * @since 3.2
218 	 */
getLaunchTimeText(IProcess proc)219 	private String getLaunchTimeText(IProcess proc) {
220 		String text = getTimeFromAttribute(proc, DebugPlugin.ATTR_LAUNCH_TIMESTAMP);
221 		if (text != null) {
222 			return text;
223 		}
224 		// TODO remove this parsing when launch time is no fixed part of label anymore
225 		Pattern pattern = Pattern.compile("\\(.*\\)"); //$NON-NLS-1$
226 		Matcher matcher = pattern.matcher(proc.getLabel());
227 		if (matcher.find()) {
228 			text = matcher.group(0);
229 		}
230 		if (text != null) {
231 			return text;
232 		}
233 		return DebugPreferencesMessages.ProcessPropertyPage_4;
234 	}
235 
236 	/**
237 	 * Try to get the terminate time for the process.
238 	 *
239 	 * @param proc the process to get terminate time for
240 	 * @return the terminate time or default replacement
241 	 */
getTerminateTimeText(IProcess proc)242 	private String getTerminateTimeText(IProcess proc) {
243 		String text = getTimeFromAttribute(proc, DebugPlugin.ATTR_TERMINATE_TIMESTAMP);
244 		return text != null ? text : DebugPreferencesMessages.ProcessPropertyPage_4;
245 	}
246 
247 	/**
248 	 * Try to process launch timestamp attribute.
249 	 *
250 	 * @param proc the process to check
251 	 * @param attr the process attribute to check for timestamp
252 	 * @return the timestamp string or <code>null</code>
253 	 * @since 3.2
254 	 */
getTimeFromAttribute(IProcess proc, String attr)255 	private String getTimeFromAttribute(IProcess proc, String attr) {
256 		if (proc == null || attr == null) {
257 			return null;
258 		}
259 		String time = proc.getAttribute(attr);
260 		if (time == null) {
261 			return null;
262 		}
263 		// check to see if the date/time is just the raw long (as a string)
264 		try {
265 			long l = Long.parseLong(time);
266 			return DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM).format(new Date(l));
267 		} catch (NumberFormatException nfe) {
268 			// not a number try to format the string so it always looks the same
269 			try {
270 				Date fdate = DateFormat.getInstance().parse(time);
271 				return DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM).format(fdate);
272 			} catch (ParseException pe) {
273 				// couldn't do it, return the raw string
274 			}
275 		}
276 		return time;
277 	}
278 
279 	/**
280 	 * Initializes the text to be displayed in the environment text widget
281 	 * @param proc
282 	 * @return the environment path or a default string never <code>null</code>
283 	 *
284 	 * @see DebugPlugin#ATTR_ENVIRONMENT
285 	 * @since 3.8
286 	 */
getEnvironment(IProcess proc)287 	String getEnvironment(IProcess proc) {
288 		String env = DebugPreferencesMessages.ProcessPropertyPage_8;
289 		if(proc != null) {
290 			String tmp = proc.getAttribute(DebugPlugin.ATTR_ENVIRONMENT);
291 			if(tmp != null) {
292 				return tmp;
293 			}
294 		}
295 		return env;
296 	}
297 
298 	/**
299 	 * Initializes the text to be displayed in the working directory text widget
300 	 *
301 	 * @param proc
302 	 * @return the text to display or a default {@link String} never <code>null</code>
303 	 *
304 	 * @see DebugPlugin#ATTR_WORKING_DIRECTORY
305 	 * @since 3.8
306 	 */
getWorkingDirectory(IProcess proc)307 	String getWorkingDirectory(IProcess proc) {
308 		String wd = DebugPreferencesMessages.ProcessPropertyPage_9;
309 		if(proc != null) {
310 			String tmp = proc.getAttribute(DebugPlugin.ATTR_WORKING_DIRECTORY);
311 			if(tmp != null) {
312 				return tmp;
313 			}
314 		}
315 		return wd;
316 	}
317 
318 	@Override
createControl(Composite parent)319 	public void createControl(Composite parent) {
320 		super.createControl(parent);
321 		PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(),	IDebugHelpContextIds.PROCESS_PROPERTY_PAGE);
322 	}
323 }
324