1 /*******************************************************************************
2 * Copyright (c) 2013 Ericsson
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * Marc-Andre Laperle - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.linuxtools
.internal
.tmf
.ui
.project
.wizards
.tracepkg
;
15 import java
.io
.ByteArrayOutputStream
;
17 import java
.io
.IOException
;
18 import java
.io
.PrintStream
;
19 import java
.util
.ArrayList
;
20 import java
.util
.Arrays
;
21 import java
.util
.List
;
23 import org
.eclipse
.core
.runtime
.CoreException
;
24 import org
.eclipse
.core
.runtime
.IStatus
;
25 import org
.eclipse
.core
.runtime
.Status
;
26 import org
.eclipse
.jface
.dialogs
.ErrorDialog
;
27 import org
.eclipse
.jface
.dialogs
.IDialogSettings
;
28 import org
.eclipse
.jface
.resource
.ImageDescriptor
;
29 import org
.eclipse
.jface
.viewers
.CheckStateChangedEvent
;
30 import org
.eclipse
.jface
.viewers
.CheckboxTreeViewer
;
31 import org
.eclipse
.jface
.viewers
.ICheckStateListener
;
32 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
33 import org
.eclipse
.jface
.wizard
.WizardPage
;
34 import org
.eclipse
.linuxtools
.internal
.tmf
.ui
.Activator
;
35 import org
.eclipse
.swt
.SWT
;
36 import org
.eclipse
.swt
.events
.SelectionAdapter
;
37 import org
.eclipse
.swt
.events
.SelectionEvent
;
38 import org
.eclipse
.swt
.events
.SelectionListener
;
39 import org
.eclipse
.swt
.layout
.GridData
;
40 import org
.eclipse
.swt
.layout
.GridLayout
;
41 import org
.eclipse
.swt
.widgets
.Button
;
42 import org
.eclipse
.swt
.widgets
.Combo
;
43 import org
.eclipse
.swt
.widgets
.Composite
;
44 import org
.eclipse
.swt
.widgets
.Event
;
45 import org
.eclipse
.swt
.widgets
.FileDialog
;
46 import org
.eclipse
.swt
.widgets
.Label
;
47 import org
.eclipse
.swt
.widgets
.Listener
;
48 import org
.eclipse
.swt
.widgets
.TreeItem
;
51 * An abstract wizard page containing common code useful for both import and
52 * export trace package wizard pages
54 * @author Marc-Andre Laperle
56 abstract public class AbstractTracePackageWizardPage
extends WizardPage
{
58 private static final int COMBO_HISTORY_LENGTH
= 5;
59 private static final String STORE_FILE_PATHS_ID
= ".STORE_FILEPATHS_ID"; //$NON-NLS-1$
61 private final String fStoreFilePathId
;
62 private final IStructuredSelection fSelection
;
64 private CheckboxTreeViewer fElementViewer
;
65 private Button fSelectAllButton
;
66 private Button fDeselectAllButton
;
67 private Combo fFilePathCombo
;
68 private Button fBrowseButton
;
71 * Create the trace package wizard page
74 * the name of the page
76 * the title for this wizard page, or null if none
78 * the image descriptor for the title of this wizard page, or
81 * the current object selection
83 protected AbstractTracePackageWizardPage(String pageName
, String title
, ImageDescriptor titleImage
, IStructuredSelection selection
) {
84 super(pageName
, title
, titleImage
);
85 fStoreFilePathId
= getName() + STORE_FILE_PATHS_ID
;
86 fSelection
= selection
;
90 * Create the element viewer
92 * @param compositeParent
93 * the parent composite
95 protected void createElementViewer(Composite compositeParent
) {
96 fElementViewer
= new CheckboxTreeViewer(compositeParent
, SWT
.SINGLE
| SWT
.H_SCROLL
| SWT
.V_SCROLL
| SWT
.BORDER
| SWT
.CHECK
);
98 fElementViewer
.addCheckStateListener(new ICheckStateListener() {
100 public void checkStateChanged(CheckStateChangedEvent event
) {
101 TracePackageElement element
= (TracePackageElement
) event
.getElement();
102 if (!element
.isEnabled()) {
103 fElementViewer
.setChecked(element
, element
.isChecked());
105 setSubtreeChecked(fElementViewer
, element
, true, event
.getChecked());
107 maintainCheckIntegrity(element
);
109 if (element
.getParent() != null) {
110 // Uncheck everything in this trace if Trace files are unchecked
111 if (element
instanceof TracePackageFilesElement
) {
112 if (!element
.isChecked()) {
113 setSubtreeChecked(fElementViewer
, element
.getParent(), false, false);
115 // Check Trace files if anything else is selected
116 } else if (element
.isChecked()) {
117 TracePackageElement parent
= element
.getParent();
118 while (parent
!= null) {
119 for (TracePackageElement e
: parent
.getChildren()) {
120 if (e
instanceof TracePackageFilesElement
) {
121 setSubtreeChecked(fElementViewer
, e
, false, true);
125 parent
= parent
.getParent();
131 updateApproximateSelectedSize();
132 updatePageCompletion();
135 private void maintainCheckIntegrity(final TracePackageElement element
) {
136 TracePackageElement parentElement
= element
.getParent();
137 boolean allChecked
= true;
138 boolean oneChecked
= false;
139 if (parentElement
!= null) {
140 if (parentElement
.getChildren() != null) {
141 for (TracePackageElement child
: parentElement
.getChildren()) {
142 boolean checked
= fElementViewer
.getChecked(child
) && !fElementViewer
.getGrayed(child
);
143 oneChecked
|= checked
;
144 allChecked
&= checked
;
147 if (oneChecked
&& !allChecked
) {
148 fElementViewer
.setGrayChecked(parentElement
, true);
150 fElementViewer
.setGrayed(parentElement
, false);
151 fElementViewer
.setChecked(parentElement
, allChecked
);
153 maintainCheckIntegrity(parentElement
);
157 GridData layoutData
= new GridData(GridData
.FILL_BOTH
);
158 fElementViewer
.getTree().setLayoutData(layoutData
);
159 fElementViewer
.setContentProvider(new TracePackageContentProvider());
160 fElementViewer
.setLabelProvider(new TracePackageLabelProvider());
164 * Create the input for the element viewer
166 * @return the input for the element viewer
168 protected abstract Object
createElementViewerInput();
171 * Create the file path group that allows the user to type or browse for a
175 * the parent composite
177 * the label to describe the file path (i.e. import/export)
178 * @param fileDialogStyle
179 * SWT.OPEN or SWT.SAVE
181 protected void createFilePathGroup(Composite parent
, String label
, final int fileDialogStyle
) {
183 Composite filePathSelectionGroup
= new Composite(parent
, SWT
.NONE
);
184 GridLayout layout
= new GridLayout();
185 layout
.numColumns
= 3;
186 filePathSelectionGroup
.setLayout(layout
);
187 filePathSelectionGroup
.setLayoutData(new GridData(
188 GridData
.HORIZONTAL_ALIGN_FILL
| GridData
.VERTICAL_ALIGN_FILL
));
190 Label destinationLabel
= new Label(filePathSelectionGroup
, SWT
.NONE
);
191 destinationLabel
.setText(label
);
193 fFilePathCombo
= new Combo(filePathSelectionGroup
, SWT
.SINGLE
195 GridData data
= new GridData(GridData
.HORIZONTAL_ALIGN_FILL
196 | GridData
.GRAB_HORIZONTAL
);
197 data
.grabExcessHorizontalSpace
= true;
198 fFilePathCombo
.setLayoutData(data
);
200 fBrowseButton
= new Button(filePathSelectionGroup
,
202 fBrowseButton
.setText(org
.eclipse
.linuxtools
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.Messages
.TracePackage_Browse
);
203 fBrowseButton
.addListener(SWT
.Selection
, new Listener() {
205 public void handleEvent(Event event
) {
206 handleFilePathBrowseButtonPressed(fileDialogStyle
);
209 setButtonLayoutData(fBrowseButton
);
213 * Update the page with the file path the current file path selection
215 abstract protected void updateWithFilePathSelection();
218 * Creates the buttons for selecting all or none of the elements.
222 * @return the button group
224 protected Composite
createButtonsGroup(Composite parent
) {
227 Composite buttonComposite
= new Composite(parent
, SWT
.NONE
);
229 GridLayout layout
= new GridLayout();
230 layout
.numColumns
= 3;
231 buttonComposite
.setLayout(layout
);
232 buttonComposite
.setLayoutData(new GridData(GridData
.VERTICAL_ALIGN_FILL
233 | GridData
.HORIZONTAL_ALIGN_FILL
));
235 fSelectAllButton
= new Button(buttonComposite
, SWT
.PUSH
);
236 fSelectAllButton
.setText(org
.eclipse
.linuxtools
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.Messages
.TracePackage_SelectAll
);
238 SelectionListener listener
= new SelectionAdapter() {
240 public void widgetSelected(SelectionEvent e
) {
241 setAllChecked(fElementViewer
, true, true);
242 updateApproximateSelectedSize();
243 updatePageCompletion();
246 fSelectAllButton
.addSelectionListener(listener
);
248 fDeselectAllButton
= new Button(buttonComposite
, SWT
.PUSH
);
249 fDeselectAllButton
.setText(org
.eclipse
.linuxtools
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.Messages
.TracePackage_DeselectAll
);
251 listener
= new SelectionAdapter() {
253 public void widgetSelected(SelectionEvent e
) {
254 setAllChecked(fElementViewer
, true, false);
255 updateApproximateSelectedSize();
256 updatePageCompletion();
259 fDeselectAllButton
.addSelectionListener(listener
);
261 return buttonComposite
;
265 * Restore widget values to the values that they held last time this wizard
266 * was used to completion.
268 protected void restoreWidgetValues() {
269 IDialogSettings settings
= getDialogSettings();
270 if (settings
!= null) {
271 String
[] directoryNames
= settings
.getArray(fStoreFilePathId
);
272 if (directoryNames
== null || directoryNames
.length
== 0) {
276 for (int i
= 0; i
< directoryNames
.length
; i
++) {
277 fFilePathCombo
.add(directoryNames
[i
]);
283 * Save widget values to Dialog settings
285 protected void saveWidgetValues() {
286 IDialogSettings settings
= getDialogSettings();
287 if (settings
!= null) {
288 // update directory names history
289 String
[] directoryNames
= settings
.getArray(fStoreFilePathId
);
290 if (directoryNames
== null) {
291 directoryNames
= new String
[0];
294 directoryNames
= addToHistory(directoryNames
, getFilePathValue());
295 settings
.put(fStoreFilePathId
, directoryNames
);
300 * Determine if the page is complete and update the page appropriately.
302 protected void updatePageCompletion() {
303 boolean pageComplete
= determinePageCompletion();
304 setPageComplete(pageComplete
);
306 setErrorMessage(null);
311 * Determine if the page is completed or not
313 * @return true if the page is completed, false otherwise
315 protected boolean determinePageCompletion() {
316 return fElementViewer
.getCheckedElements().length
> 0 && !getFilePathValue().isEmpty();
320 * Handle error status
325 protected void handleErrorStatus(IStatus status
) {
327 Throwable exception
= status
.getException();
328 String message
= status
.getMessage().length() > 0 ? status
.getMessage() : org
.eclipse
.linuxtools
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.Messages
.TracePackage_ErrorOperation
;
330 if (!status
.isMultiStatus()) {
331 handleError(message
, exception
);
335 // Build a string with all the children status messages, exception
336 // messages and stack traces
337 StringBuilder sb
= new StringBuilder();
338 for (IStatus childStatus
: status
.getChildren()) {
339 StringBuilder childSb
= new StringBuilder();
340 if (!childStatus
.getMessage().isEmpty()) {
341 childSb
.append(childStatus
.getMessage() + '\n');
344 Throwable childException
= childStatus
.getException();
345 if (childException
!= null) {
346 String reason
= childException
.getMessage();
347 // Some system exceptions have no message
348 if (reason
== null) {
349 reason
= childException
.toString();
352 String stackMessage
= getExceptionStackMessage(childException
);
353 if (stackMessage
== null) {
354 stackMessage
= reason
;
357 childSb
.append(stackMessage
);
360 if (childSb
.length() > 0) {
361 childSb
.insert(0, '\n');
362 sb
.append(childSb
.toString());
366 // ErrorDialog only prints the call stack for a CoreException
367 exception
= new CoreException(new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, IStatus
.ERROR
, sb
.toString(), null));
368 final Status statusWithException
= new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, IStatus
.ERROR
, org
.eclipse
.linuxtools
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.Messages
.TracePackage_ErrorMultipleProblems
, exception
);
370 Activator
.getDefault().logError(message
, exception
);
371 ErrorDialog
.openError(getContainer().getShell(), org
.eclipse
.linuxtools
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.Messages
.TracePackage_InternalErrorTitle
, message
, statusWithException
);
375 * Handle errors occurring in the wizard operations
380 * the exception attached to the message
382 protected void handleError(String message
, Throwable exception
) {
383 Activator
.getDefault().logError(message
, exception
);
384 displayErrorDialog(message
, exception
);
387 private static String
getExceptionStackMessage(Throwable exception
) {
388 String stackMessage
= null;
389 ByteArrayOutputStream baos
= new ByteArrayOutputStream();
390 PrintStream ps
= new PrintStream(baos
);
391 exception
.printStackTrace(ps
);
395 stackMessage
= baos
.toString();
396 } catch (IOException e
) {
402 private void displayErrorDialog(String message
, Throwable exception
) {
403 if (exception
== null) {
404 final Status s
= new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, message
);
405 ErrorDialog
.openError(getContainer().getShell(), org
.eclipse
.linuxtools
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.Messages
.TracePackage_InternalErrorTitle
, null, s
);
409 String reason
= exception
.getMessage();
410 // Some system exceptions have no message
411 if (reason
== null) {
412 reason
= exception
.toString();
415 String stackMessage
= getExceptionStackMessage(exception
);
416 if (stackMessage
== null || stackMessage
.isEmpty()) {
417 stackMessage
= reason
;
420 // ErrorDialog only prints the call stack for a CoreException
421 CoreException coreException
= new CoreException(new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, IStatus
.ERROR
, stackMessage
, exception
));
422 final Status s
= new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, IStatus
.ERROR
, reason
, coreException
);
423 ErrorDialog
.openError(getContainer().getShell(), org
.eclipse
.linuxtools
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.Messages
.TracePackage_InternalErrorTitle
, message
, s
);
427 * A version of setSubtreeChecked that is aware of isEnabled
434 * if only enabled elements should be considered
436 * true if the item should be checked, and false if it should be
439 protected static void setSubtreeChecked(CheckboxTreeViewer viewer
, TracePackageElement element
, boolean enabledOnly
, boolean checked
) {
440 if (!enabledOnly
|| element
.isEnabled()) {
441 viewer
.setChecked(element
, checked
);
443 viewer
.setGrayed(element
, false);
445 element
.setChecked(checked
);
446 if (element
.getChildren() != null) {
447 for (TracePackageElement child
: element
.getChildren()) {
448 setSubtreeChecked(viewer
, child
, enabledOnly
, checked
);
455 * Sets all items in the element viewer to be checked or unchecked
460 * if only enabled elements should be considered
462 * whether or not items should be checked
464 protected static void setAllChecked(CheckboxTreeViewer viewer
, boolean enabledOnly
, boolean checked
) {
465 TreeItem
[] items
= viewer
.getTree().getItems();
466 for (int i
= 0; i
< items
.length
; i
++) {
467 Object element
= items
[i
].getData();
468 setSubtreeChecked(viewer
, (TracePackageElement
) element
, enabledOnly
, checked
);
472 private static void addToHistory(List
<String
> history
, String newEntry
) {
473 history
.remove(newEntry
);
474 history
.add(0, newEntry
);
476 // since only one new item was added, we can be over the limit
477 // by at most one item
478 if (history
.size() > COMBO_HISTORY_LENGTH
) {
479 history
.remove(COMBO_HISTORY_LENGTH
);
483 private static String
[] addToHistory(String
[] history
, String newEntry
) {
484 ArrayList
<String
> l
= new ArrayList
<>(Arrays
.asList(history
));
485 addToHistory(l
, newEntry
);
486 String
[] r
= new String
[l
.size()];
492 * Open an appropriate file dialog so that the user can specify a file to
494 * @param fileDialogStyle
496 private void handleFilePathBrowseButtonPressed(int fileDialogStyle
) {
497 FileDialog dialog
= new FileDialog(getContainer().getShell(), fileDialogStyle
| SWT
.SHEET
);
498 dialog
.setFilterExtensions(new String
[] { "*.zip;*.tar.gz;*.tar;*.tgz", "*.*" }); //$NON-NLS-1$ //$NON-NLS-2$
499 dialog
.setText(Messages
.TracePackage_FileDialogTitle
);
500 String currentSourceString
= getFilePathValue();
501 int lastSeparatorIndex
= currentSourceString
.lastIndexOf(File
.separator
);
502 if (lastSeparatorIndex
!= -1) {
503 dialog
.setFilterPath(currentSourceString
.substring(0, lastSeparatorIndex
));
505 String selectedFileName
= dialog
.open();
507 if (selectedFileName
!= null) {
508 setFilePathValue(selectedFileName
);
509 updateWithFilePathSelection();
514 * Get the current file path value
516 * @return the current file path value
518 protected String
getFilePathValue() {
519 return fFilePathCombo
.getText().trim();
523 * Set the file path value
528 protected void setFilePathValue(String value
) {
529 fFilePathCombo
.setText(value
);
530 updatePageCompletion();
534 * Update the approximate size of the selected elements
536 protected void updateApproximateSelectedSize() {
540 * Get the element tree viewer
542 * @return the element tree viewer
544 protected CheckboxTreeViewer
getElementViewer() {
545 return fElementViewer
;
549 * Get the file path combo box
551 * @return the file path combo box
553 protected Combo
getFilePathCombo() {
554 return fFilePathCombo
;
558 * Get the object selection when the wizard was created
560 * @return the object selection
562 protected IStructuredSelection
getSelection() {