1 /*******************************************************************************
2 * Copyright (c) 2013, 2014 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
.tracecompass
.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
.swt
.SWT
;
35 import org
.eclipse
.swt
.events
.SelectionAdapter
;
36 import org
.eclipse
.swt
.events
.SelectionEvent
;
37 import org
.eclipse
.swt
.events
.SelectionListener
;
38 import org
.eclipse
.swt
.layout
.GridData
;
39 import org
.eclipse
.swt
.layout
.GridLayout
;
40 import org
.eclipse
.swt
.widgets
.Button
;
41 import org
.eclipse
.swt
.widgets
.Combo
;
42 import org
.eclipse
.swt
.widgets
.Composite
;
43 import org
.eclipse
.swt
.widgets
.Event
;
44 import org
.eclipse
.swt
.widgets
.FileDialog
;
45 import org
.eclipse
.swt
.widgets
.Label
;
46 import org
.eclipse
.swt
.widgets
.Listener
;
47 import org
.eclipse
.swt
.widgets
.TreeItem
;
48 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.Activator
;
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 public abstract 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 if (fElementViewer
.getGrayed(child
)) {
147 boolean checked
= fElementViewer
.getChecked(child
);
148 oneChecked
|= checked
;
149 allChecked
&= checked
;
152 if (oneChecked
&& !allChecked
) {
153 fElementViewer
.setGrayChecked(parentElement
, true);
155 fElementViewer
.setGrayed(parentElement
, false);
156 fElementViewer
.setChecked(parentElement
, allChecked
);
158 maintainCheckIntegrity(parentElement
);
162 GridData layoutData
= new GridData(GridData
.FILL_BOTH
);
163 fElementViewer
.getTree().setLayoutData(layoutData
);
164 fElementViewer
.setContentProvider(new TracePackageContentProvider());
165 fElementViewer
.setLabelProvider(new TracePackageLabelProvider());
169 * Create the input for the element viewer
171 * @return the input for the element viewer
173 protected abstract Object
createElementViewerInput();
176 * Create the file path group that allows the user to type or browse for a
180 * the parent composite
182 * the label to describe the file path (i.e. import/export)
183 * @param fileDialogStyle
184 * SWT.OPEN or SWT.SAVE
186 protected void createFilePathGroup(Composite parent
, String label
, final int fileDialogStyle
) {
188 Composite filePathSelectionGroup
= new Composite(parent
, SWT
.NONE
);
189 GridLayout layout
= new GridLayout();
190 layout
.numColumns
= 3;
191 filePathSelectionGroup
.setLayout(layout
);
192 filePathSelectionGroup
.setLayoutData(new GridData(
193 GridData
.HORIZONTAL_ALIGN_FILL
| GridData
.VERTICAL_ALIGN_FILL
));
195 Label destinationLabel
= new Label(filePathSelectionGroup
, SWT
.NONE
);
196 destinationLabel
.setText(label
);
198 fFilePathCombo
= new Combo(filePathSelectionGroup
, SWT
.SINGLE
200 GridData data
= new GridData(GridData
.HORIZONTAL_ALIGN_FILL
201 | GridData
.GRAB_HORIZONTAL
);
202 data
.grabExcessHorizontalSpace
= true;
203 fFilePathCombo
.setLayoutData(data
);
205 fBrowseButton
= new Button(filePathSelectionGroup
,
207 fBrowseButton
.setText(org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.Messages
.TracePackage_Browse
);
208 fBrowseButton
.addListener(SWT
.Selection
, new Listener() {
210 public void handleEvent(Event event
) {
211 handleFilePathBrowseButtonPressed(fileDialogStyle
);
214 setButtonLayoutData(fBrowseButton
);
218 * Update the page with the file path the current file path selection
220 protected abstract void updateWithFilePathSelection();
223 * Creates the buttons for selecting all or none of the elements.
227 * @return the button group
229 protected Composite
createButtonsGroup(Composite parent
) {
232 Composite buttonComposite
= new Composite(parent
, SWT
.NONE
);
234 GridLayout layout
= new GridLayout();
235 layout
.numColumns
= 3;
236 buttonComposite
.setLayout(layout
);
237 buttonComposite
.setLayoutData(new GridData(GridData
.VERTICAL_ALIGN_FILL
238 | GridData
.HORIZONTAL_ALIGN_FILL
));
240 fSelectAllButton
= new Button(buttonComposite
, SWT
.PUSH
);
241 fSelectAllButton
.setText(org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.Messages
.TracePackage_SelectAll
);
243 SelectionListener listener
= new SelectionAdapter() {
245 public void widgetSelected(SelectionEvent e
) {
246 setAllChecked(fElementViewer
, true, true);
247 updateApproximateSelectedSize();
248 updatePageCompletion();
251 fSelectAllButton
.addSelectionListener(listener
);
253 fDeselectAllButton
= new Button(buttonComposite
, SWT
.PUSH
);
254 fDeselectAllButton
.setText(org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.Messages
.TracePackage_DeselectAll
);
256 listener
= new SelectionAdapter() {
258 public void widgetSelected(SelectionEvent e
) {
259 setAllChecked(fElementViewer
, true, false);
260 updateApproximateSelectedSize();
261 updatePageCompletion();
264 fDeselectAllButton
.addSelectionListener(listener
);
266 return buttonComposite
;
270 * Restore widget values to the values that they held last time this wizard
271 * was used to completion.
273 protected void restoreWidgetValues() {
274 IDialogSettings settings
= getDialogSettings();
275 if (settings
!= null) {
276 String
[] directoryNames
= settings
.getArray(fStoreFilePathId
);
277 if (directoryNames
== null || directoryNames
.length
== 0) {
281 for (int i
= 0; i
< directoryNames
.length
; i
++) {
282 fFilePathCombo
.add(directoryNames
[i
]);
288 * Save widget values to Dialog settings
290 protected void saveWidgetValues() {
291 IDialogSettings settings
= getDialogSettings();
292 if (settings
!= null) {
293 // update directory names history
294 String
[] directoryNames
= settings
.getArray(fStoreFilePathId
);
295 if (directoryNames
== null) {
296 directoryNames
= new String
[0];
299 directoryNames
= addToHistory(directoryNames
, getFilePathValue());
300 settings
.put(fStoreFilePathId
, directoryNames
);
305 * Determine if the page is complete and update the page appropriately.
307 protected void updatePageCompletion() {
308 boolean pageComplete
= determinePageCompletion();
309 setPageComplete(pageComplete
);
311 setErrorMessage(null);
316 * Determine if the page is completed or not
318 * @return true if the page is completed, false otherwise
320 protected boolean determinePageCompletion() {
321 return fElementViewer
.getCheckedElements().length
> 0 && !getFilePathValue().isEmpty();
325 * Handle error status
330 protected void handleErrorStatus(IStatus status
) {
332 Throwable exception
= status
.getException();
333 String message
= status
.getMessage().length() > 0 ? status
.getMessage() : org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.Messages
.TracePackage_ErrorOperation
;
335 if (!status
.isMultiStatus()) {
336 handleError(message
, exception
);
340 // Build a string with all the children status messages, exception
341 // messages and stack traces
342 StringBuilder sb
= new StringBuilder();
343 for (IStatus childStatus
: status
.getChildren()) {
344 StringBuilder childSb
= new StringBuilder();
345 if (!childStatus
.getMessage().isEmpty()) {
346 childSb
.append(childStatus
.getMessage() + '\n');
349 Throwable childException
= childStatus
.getException();
350 if (childException
!= null) {
351 String reason
= childException
.getMessage();
352 // Some system exceptions have no message
353 if (reason
== null) {
354 reason
= childException
.toString();
357 String stackMessage
= getExceptionStackMessage(childException
);
358 if (stackMessage
== null) {
359 stackMessage
= reason
;
362 childSb
.append(stackMessage
);
365 if (childSb
.length() > 0) {
366 childSb
.insert(0, '\n');
367 sb
.append(childSb
.toString());
371 // ErrorDialog only prints the call stack for a CoreException
372 exception
= new CoreException(new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, IStatus
.ERROR
, sb
.toString(), null));
373 final Status statusWithException
= new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, IStatus
.ERROR
, org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.Messages
.TracePackage_ErrorMultipleProblems
, exception
);
375 Activator
.getDefault().logError(message
, exception
);
376 ErrorDialog
.openError(getContainer().getShell(), org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.Messages
.TracePackage_InternalErrorTitle
, message
, statusWithException
);
380 * Handle errors occurring in the wizard operations
385 * the exception attached to the message
387 protected void handleError(String message
, Throwable exception
) {
388 Activator
.getDefault().logError(message
, exception
);
389 displayErrorDialog(message
, exception
);
392 private static String
getExceptionStackMessage(Throwable exception
) {
393 String stackMessage
= null;
394 ByteArrayOutputStream baos
= new ByteArrayOutputStream();
395 PrintStream ps
= new PrintStream(baos
);
396 exception
.printStackTrace(ps
);
400 stackMessage
= baos
.toString();
401 } catch (IOException e
) {
407 private void displayErrorDialog(String message
, Throwable exception
) {
408 if (exception
== null) {
409 final Status s
= new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, message
);
410 ErrorDialog
.openError(getContainer().getShell(), org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.Messages
.TracePackage_InternalErrorTitle
, null, s
);
414 String reason
= exception
.getMessage();
415 // Some system exceptions have no message
416 if (reason
== null) {
417 reason
= exception
.toString();
420 String stackMessage
= getExceptionStackMessage(exception
);
421 if (stackMessage
== null || stackMessage
.isEmpty()) {
422 stackMessage
= reason
;
425 // ErrorDialog only prints the call stack for a CoreException
426 CoreException coreException
= new CoreException(new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, IStatus
.ERROR
, stackMessage
, exception
));
427 final Status s
= new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, IStatus
.ERROR
, reason
, coreException
);
428 ErrorDialog
.openError(getContainer().getShell(), org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.Messages
.TracePackage_InternalErrorTitle
, message
, s
);
432 * A version of setSubtreeChecked that is aware of isEnabled
439 * if only enabled elements should be considered
441 * true if the item should be checked, and false if it should be
444 protected static void setSubtreeChecked(CheckboxTreeViewer viewer
, TracePackageElement element
, boolean enabledOnly
, boolean checked
) {
445 if (!enabledOnly
|| element
.isEnabled()) {
446 viewer
.setChecked(element
, checked
);
448 viewer
.setGrayed(element
, false);
450 element
.setChecked(checked
);
451 if (element
.getChildren() != null) {
452 for (TracePackageElement child
: element
.getChildren()) {
453 setSubtreeChecked(viewer
, child
, enabledOnly
, checked
);
460 * Sets all items in the element viewer to be checked or unchecked
465 * if only enabled elements should be considered
467 * whether or not items should be checked
469 protected static void setAllChecked(CheckboxTreeViewer viewer
, boolean enabledOnly
, boolean checked
) {
470 TreeItem
[] items
= viewer
.getTree().getItems();
471 for (int i
= 0; i
< items
.length
; i
++) {
472 Object element
= items
[i
].getData();
473 setSubtreeChecked(viewer
, (TracePackageElement
) element
, enabledOnly
, checked
);
477 private static void addToHistory(List
<String
> history
, String newEntry
) {
478 history
.remove(newEntry
);
479 history
.add(0, newEntry
);
481 // since only one new item was added, we can be over the limit
482 // by at most one item
483 if (history
.size() > COMBO_HISTORY_LENGTH
) {
484 history
.remove(COMBO_HISTORY_LENGTH
);
488 private static String
[] addToHistory(String
[] history
, String newEntry
) {
489 ArrayList
<String
> l
= new ArrayList
<>(Arrays
.asList(history
));
490 addToHistory(l
, newEntry
);
491 String
[] r
= new String
[l
.size()];
497 * Open an appropriate file dialog so that the user can specify a file to
499 * @param fileDialogStyle
501 private void handleFilePathBrowseButtonPressed(int fileDialogStyle
) {
502 FileDialog dialog
= new FileDialog(getContainer().getShell(), fileDialogStyle
| SWT
.SHEET
);
503 dialog
.setFilterExtensions(new String
[] { "*.zip;*.tar.gz;*.tar;*.tgz", "*.*" }); //$NON-NLS-1$ //$NON-NLS-2$
504 dialog
.setText(Messages
.TracePackage_FileDialogTitle
);
505 String currentSourceString
= getFilePathValue();
506 int lastSeparatorIndex
= currentSourceString
.lastIndexOf(File
.separator
);
507 if (lastSeparatorIndex
!= -1) {
508 dialog
.setFilterPath(currentSourceString
.substring(0, lastSeparatorIndex
));
510 String selectedFileName
= dialog
.open();
512 if (selectedFileName
!= null) {
513 setFilePathValue(selectedFileName
);
514 updateWithFilePathSelection();
519 * Get the current file path value
521 * @return the current file path value
523 protected String
getFilePathValue() {
524 return fFilePathCombo
.getText().trim();
528 * Set the file path value
533 protected void setFilePathValue(String value
) {
534 fFilePathCombo
.setText(value
);
535 updatePageCompletion();
539 * Update the approximate size of the selected elements
541 protected void updateApproximateSelectedSize() {
545 * Get the element tree viewer
547 * @return the element tree viewer
549 protected CheckboxTreeViewer
getElementViewer() {
550 return fElementViewer
;
554 * Get the file path combo box
556 * @return the file path combo box
558 protected Combo
getFilePathCombo() {
559 return fFilePathCombo
;
563 * Get the object selection when the wizard was created
565 * @return the object selection
567 protected IStructuredSelection
getSelection() {