/*******************************************************************************
* Copyright (c) 2013, 2014 Ericsson
*
* 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:
* Matthew Khouzam - Initial API and implementation
*******************************************************************************/
package fr.inria.linuxtools.internal.tmf.ui.project.wizards.importtrace;
import java.io.File;
import java.text.DecimalFormat;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.ColumnViewerEditor;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.FocusCellOwnerDrawHighlighter;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.TreeViewerEditor;
import org.eclipse.jface.viewers.TreeViewerFocusCellManager;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
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.ui.IWorkbench;
import fr.inria.linuxtools.internal.tmf.ui.Activator;
import fr.inria.linuxtools.internal.tmf.ui.ITmfImageConstants;
import fr.inria.linuxtools.tmf.core.project.model.TmfTraceType;
import fr.inria.linuxtools.tmf.core.project.model.TraceValidationHelper;
/**
* <b>Import page that scans files, can be cancelled</b> this page is the third
* of three pages shown. This one selects the traces to be imported that are to
* be scanned.
*
* @author Matthew Khouzam
* @since 2.0
*/
public class ImportTraceWizardScanPage extends AbstractImportTraceWizardPage {
private static final int COL_WIDTH = 200;
private static final int MAX_TRACES = 65536;
private CheckboxTreeViewer traceTypeViewer;
final ScanRunnable fRunnable = new ScanRunnable("Scan job"); //$NON-NLS-1$
final private BlockingQueue<TraceValidationHelper> fTracesToScan = new ArrayBlockingQueue<>(MAX_TRACES);
private volatile boolean fCanRun = true;
// --------------------------------------------------------------------------------
// Constructor and destructor
// --------------------------------------------------------------------------------
/**
* Import page that scans files, can be cancelled.
*
* @param name
* The name of the page.
* @param selection
* The current selection
*/
protected ImportTraceWizardScanPage(String name, IStructuredSelection selection) {
super(name, selection);
}
/**
* Import page that scans files, can be cancelled
*
* @param workbench
* The workbench reference.
* @param selection
* The current selection
*/
public ImportTraceWizardScanPage(IWorkbench workbench, IStructuredSelection selection) {
super(workbench, selection);
}
@Override
public void dispose() {
fCanRun = false;
fRunnable.done(Status.OK_STATUS);
super.dispose();
}
/*
* Init
*/
@Override
public void createControl(Composite parent) {
super.createControl(parent);
final Composite control = (Composite) this.getControl();
setTitle(Messages.ImportTraceWizardScanPageTitle);
traceTypeViewer = new CheckboxTreeViewer(control, SWT.CHECK);
traceTypeViewer.setContentProvider(getBatchWizard().getScannedTraces());
traceTypeViewer.getTree().setHeaderVisible(true);
traceTypeViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
traceTypeViewer.setInput(getBatchWizard().getScannedTraces());
traceTypeViewer.addCheckStateListener(new ImportTraceCheckStateListener());
TreeViewerFocusCellManager focusCellManager = new TreeViewerFocusCellManager(traceTypeViewer, new FocusCellOwnerDrawHighlighter(traceTypeViewer));
ColumnViewerEditorActivationStrategy actSupport = new ColumnViewerEditorActivationStrategy(traceTypeViewer) {
};
TreeViewerEditor.create(traceTypeViewer, focusCellManager, actSupport, ColumnViewerEditor.TABBING_HORIZONTAL
| ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR
| ColumnViewerEditor.TABBING_VERTICAL | ColumnViewerEditor.KEYBOARD_ACTIVATION);
final TextCellEditor textCellEditor = new TextCellEditor(traceTypeViewer.getTree());
// --------------------
// Column 1
// --------------------
TreeViewerColumn column = new TreeViewerColumn(traceTypeViewer, SWT.NONE);
column.getColumn().setWidth(COL_WIDTH);
column.getColumn().setText(Messages.ImportTraceWizardTraceDisplayName);
column.setLabelProvider(new FirstColumnLabelProvider());
column.setEditingSupport(new ColumnEditorSupport(traceTypeViewer, textCellEditor));
// --------------------
// Column 2
// --------------------
column = new TreeViewerColumn(traceTypeViewer, SWT.NONE);
column.getColumn().setWidth(500);
column.getColumn().setText(Messages.ImportTraceWizardImportCaption);
column.setLabelProvider(new ColumnLabelProvider() {
@Override
public String getText(Object element) {
if (element instanceof FileAndName) {
FileAndName elem = (FileAndName) element;
return elem.getFile().getPath();
}
return null;
}
});
// --------------------
// Column 3
// --------------------
column = new TreeViewerColumn(traceTypeViewer, SWT.NONE);
column.getColumn().setWidth(80);
column.getColumn().setText(Messages.ImportTraceWizardScanPageSize);
column.getColumn().setAlignment(SWT.RIGHT);
column.setLabelProvider(new ColumnLabelProvider() {
@Override
public String getText(Object element) {
if (element instanceof FileAndName) {
FileAndName elem = (FileAndName) element;
long len = recurseSize(elem.getFile());
if (len > 0) {
double sizeb10 = Math.log10(len);
DecimalFormat df = new DecimalFormat();
df.setMaximumFractionDigits(2);
df.setMinimumFractionDigits(0);
if (sizeb10 > 12) {
final double tbSize = len / 1024.0 / 1024 / 1024 / 1024;
return df.format(tbSize) + Messages.ImportTraceWizardScanPageTerabyte;
}
if (sizeb10 > 9) {
final double gbSize = len / 1024.0 / 1024 / 1024;
return df.format(gbSize) + Messages.ImportTraceWizardScanPageGigabyte;
}
if (sizeb10 > 6) {
final double mbSize = len / 1024.0 / 1024;
return df.format(mbSize) + Messages.ImportTraceWizardScanPageMegabyte;
}
if (sizeb10 > 3) {
final double kbSize = len / 1024.0;
return df.format(kbSize) + Messages.ImportTraceWizardScanPageKilobyte;
}
}
return Long.toString(len) + Messages.ImportTraceWizardScanPagebyte;
}
return null;
}
private long recurseSize(File file) {
if (file.isFile() && file.canRead()) {
return file.length();
}
long size = 0;
if (file.exists() && file.isDirectory() && file.canRead()) {
final File[] listFiles = file.listFiles();
if (listFiles != null) {
for (File child : listFiles) {
if (child.isFile() && child.canRead()) {
size += child.length();
} else if (child.isDirectory()) {
size += recurseSize(child);
} else {
Activator.getDefault().logError("Unknown \"file\" type for " + child + ' ' + child.toString()); //$NON-NLS-1$
}
}
}
}
return size;
}
});
init();
getBatchWizard().setTracesToScan(fTracesToScan);
getBatchWizard().setTraceFolder(fTargetFolder);
fRunnable.schedule();
setErrorMessage(Messages.ImportTraceWizardScanPageSelectAtleastOne);
}
private void init() {
Composite optionPane = (Composite) this.getControl();
optionPane.setLayout(new GridLayout());
optionPane.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, true, true));
final Button fLink = new Button(optionPane, SWT.RADIO);
fLink.setText(Messages.ImportTraceWizardLinkTraces);
fLink.setSelection(true);
fLink.setLayoutData(new GridData());
final Button fCopy = new Button(optionPane, SWT.RADIO);
fCopy.setText(Messages.ImportTraceWizardCopyTraces);
fCopy.setLayoutData(new GridData());
final SelectionListener linkedListener = new RadioChooser(fLink);
fLink.addSelectionListener(linkedListener);
fCopy.addSelectionListener(linkedListener);
Button fOverwrite = new Button(optionPane, SWT.CHECK);
fOverwrite.setText(Messages.ImportTraceWizardOverwriteTraces);
fOverwrite.setLayoutData(new GridData());
fOverwrite.setSelection(true);
fOverwrite.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent e) {
getBatchWizard().setOverwrite(((Button) e.widget).getSelection());
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
}
});
}
/*
* Helper classes
*/
private final class RadioChooser implements SelectionListener {
final private Button isLinked;
public RadioChooser(Button desiredButton) {
isLinked = desiredButton;
}
@Override
public void widgetSelected(SelectionEvent e) {
final Button widget = (Button) e.widget;
getBatchWizard().setLinked(widget.equals(isLinked));
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
}
}
private final class ColumnEditorSupport extends EditingSupport {
private final TextCellEditor textCellEditor;
private ColumnEditorSupport(ColumnViewer viewer, TextCellEditor textCellEditor) {
super(viewer);
this.textCellEditor = textCellEditor;
}
@Override
protected boolean canEdit(Object element) {
return element instanceof FileAndName;
}
@Override
protected CellEditor getCellEditor(Object element) {
return textCellEditor;
}
@Override
protected Object getValue(Object element) {
if (element instanceof FileAndName) {
return ((FileAndName) element).getName();
}
return null;
}
@Override
protected void setValue(Object element, Object value) {
FileAndName fan = (FileAndName) element;
fan.setName((String) value);
getBatchWizard().updateConflicts();
traceTypeViewer.update(element, null);
traceTypeViewer.refresh();
}
}
private final class FirstColumnLabelProvider extends ColumnLabelProvider {
Image fConflict;
@Override
public Image getImage(Object element) {
if (element instanceof FileAndName) {
final FileAndName fan = (FileAndName) element;
if (fan.isConflictingName()) {
if (fConflict == null) {
fConflict = Activator.getDefault().getImageFromImageRegistry(ITmfImageConstants.IMG_UI_CONFLICT);
}
return fConflict;
}
}
return null;
}
@Override
public String getText(Object element) {
if (element instanceof FileAndName) {
FileAndName elem = (FileAndName) element;
return elem.getName();
}
if (element instanceof String) {
return (String) element;
}
return null;
}
}
private final class ImportTraceCheckStateListener implements ICheckStateListener {
@Override
public void checkStateChanged(CheckStateChangedEvent event) {
final CheckboxTreeViewer tv = (CheckboxTreeViewer)
event.getSource();
if (event.getElement() instanceof FileAndName) {
final FileAndName element = (FileAndName) event.getElement();
if (event.getChecked()) {
getBatchWizard().addFileToImport(element);
traceTypeViewer.update(element, null);
}
else {
getBatchWizard().removeFileToImport(element);
traceTypeViewer.update(element, null);
}
maintainCheckIntegrity(tv, element);
}
if (event.getElement() instanceof String) {
tv.setSubtreeChecked(event.getElement(), event.getChecked());
final Object[] children =
getBatchWizard().getScannedTraces().getChildren(event.getElement());
if (event.getChecked()) {
for (int i = 0; i < children.length; i++) {
final FileAndName element = (FileAndName) children[i];
getBatchWizard().addFileToImport(element);
traceTypeViewer.update(children[i], null);
}
}
else {
for (int i = 0; i < children.length; i++) {
getBatchWizard().removeFileToImport((FileAndName) children[i]);
}
}
}
getBatchWizard().updateConflicts();
if (getBatchWizard().hasConflicts()) {
setErrorMessage(Messages.ImportTraceWizardScanPageRenameError);
} else if (!getBatchWizard().hasTracesToImport()) {
setErrorMessage(Messages.ImportTraceWizardScanPageSelectAtleastOne);
} else {
setErrorMessage(null);
}
getWizard().getContainer().updateButtons();
traceTypeViewer.update(event.getElement(), null);
}
private void maintainCheckIntegrity(final CheckboxTreeViewer viewer, final FileAndName element) {
final ImportTraceContentProvider scannedTraces = getBatchWizard().getScannedTraces();
String parentElement = (String) scannedTraces.getParent(element);
boolean allChecked = true;
final FileAndName[] siblings = scannedTraces.getSiblings(element);
if (siblings != null) {
for (FileAndName child : siblings) {
allChecked &= viewer.getChecked(child);
}
}
viewer.setChecked(parentElement, allChecked);
}
}
private final class ScanRunnable extends Job {
// monitor is stored here, starts as the main monitor but becomes a
// submonitor
private IProgressMonitor fMonitor;
public ScanRunnable(String name) {
super(name);
this.setSystem(true);
}
private synchronized IProgressMonitor getMonitor() {
return fMonitor;
}
@Override
public IStatus run(IProgressMonitor monitor) {
/*
* Set up phase, it is synchronous
*/
fMonitor = monitor;
final Control control = traceTypeViewer.getControl();
// please note the sync exec here is to allow us to set
control.getDisplay().syncExec(new Runnable() {
@Override
public void run() {
// monitor gets overwritten here so it's necessary to save
// it in a field.
fMonitor = SubMonitor.convert(getMonitor());
getMonitor().setTaskName(Messages.ImportTraceWizardPageScanScanning + ' ');
((SubMonitor) getMonitor()).setWorkRemaining(IProgressMonitor.UNKNOWN);
}
});
/*
* At this point we start calling async execs and updating the view.
* This is a good candidate to parallelise.
*/
while (fCanRun == true) {
boolean updated = false;
boolean validCombo;
if (fTracesToScan.isEmpty() && !control.isDisposed()) {
control.getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
if (!control.isDisposed()) {
getMonitor().setTaskName(Messages.ImportTraceWizardPageScanScanning + ' ');
getMonitor().subTask(Messages.ImportTraceWizardPageScanDone);
ImportTraceWizardScanPage.this.setMessage(Messages.ImportTraceWizardPageScanScanning + ' ' + Messages.ImportTraceWizardPageScanDone);
}
}
});
}
try {
final TraceValidationHelper traceToScan = fTracesToScan.take();
if (!getBatchWizard().hasScanned(traceToScan)) {
getBatchWizard().addResult(traceToScan, TmfTraceType.validate(traceToScan));
}
/*
* The following is to update the UI
*/
validCombo = getBatchWizard().getResult(traceToScan);
if (validCombo) {
// Synched on it's parent
getBatchWizard().getScannedTraces().addCandidate(traceToScan.getTraceType(), new File(traceToScan.getTraceToScan()));
updated = true;
}
final int scanned = getBatchWizard().getNumberOfResults();
final int total = scanned + fTracesToScan.size();
final int prevVal = (int) ((scanned - 1) * 100.0 / total);
final int curVal = (int) ((scanned) * 100.0 / total);
if (curVal != prevVal) {
updated = true;
}
/*
* update the progress
*/
if (updated) {
if (!control.isDisposed()) {
control.getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
if (!control.isDisposed()) {
getMonitor().setTaskName(Messages.ImportTraceWizardPageScanScanning + ' ');
getMonitor().subTask(traceToScan.getTraceToScan());
getMonitor().worked(1);
ImportTraceWizardScanPage.this.setMessage(Messages.ImportTraceWizardPageScanScanning + ' '
+ Integer.toString(curVal)
+ '%');
}
}
}
);
}
}
/*
* here we update the table
*/
final boolean editing = traceTypeViewer.isCellEditorActive();
if (updated && !editing) {
if (!control.isDisposed()) {
control.getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
if (!control.isDisposed()) {
if (!traceTypeViewer.isCellEditorActive()) {
traceTypeViewer.refresh();
}
}
}
});
}
}
} catch (InterruptedException e) {
return new Status(IStatus.CANCEL, Activator.PLUGIN_ID, new String());
}
}
return Status.OK_STATUS;
}
}
/**
* Refresh the view and the corresponding model.
*/
public void refresh() {
final Control control = traceTypeViewer.getControl();
if (!control.isDisposed()) {
control.getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
if (!control.isDisposed()) {
traceTypeViewer.refresh();
}
}
});
}
}
}