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 * Matthew Khouzam - Initial API and implementation
11 * Marc-Andre Laperle - Log some exceptions
12 * Patrick Tasse - Add support for source location
13 *******************************************************************************/
15 package org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.importtrace
;
18 import java
.io
.FileInputStream
;
19 import java
.lang
.reflect
.InvocationTargetException
;
20 import java
.util
.ArrayList
;
21 import java
.util
.Collections
;
22 import java
.util
.Comparator
;
23 import java
.util
.HashMap
;
24 import java
.util
.HashSet
;
25 import java
.util
.Iterator
;
26 import java
.util
.List
;
29 import java
.util
.TreeSet
;
30 import java
.util
.concurrent
.BlockingQueue
;
32 import org
.eclipse
.core
.resources
.IFile
;
33 import org
.eclipse
.core
.resources
.IFolder
;
34 import org
.eclipse
.core
.resources
.IResource
;
35 import org
.eclipse
.core
.resources
.ResourcesPlugin
;
36 import org
.eclipse
.core
.runtime
.CoreException
;
37 import org
.eclipse
.core
.runtime
.IPath
;
38 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
39 import org
.eclipse
.core
.runtime
.IStatus
;
40 import org
.eclipse
.core
.runtime
.NullProgressMonitor
;
41 import org
.eclipse
.core
.runtime
.Path
;
42 import org
.eclipse
.core
.runtime
.Status
;
43 import org
.eclipse
.core
.runtime
.SubMonitor
;
44 import org
.eclipse
.core
.runtime
.URIUtil
;
45 import org
.eclipse
.jface
.dialogs
.ErrorDialog
;
46 import org
.eclipse
.jface
.dialogs
.IDialogSettings
;
47 import org
.eclipse
.jface
.operation
.IRunnableWithProgress
;
48 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
49 import org
.eclipse
.jface
.wizard
.IWizardPage
;
50 import org
.eclipse
.jface
.wizard
.WizardDialog
;
51 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.Activator
;
52 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.model
.TmfImportHelper
;
53 import org
.eclipse
.tracecompass
.tmf
.core
.TmfCommonConstants
;
54 import org
.eclipse
.tracecompass
.tmf
.core
.project
.model
.TmfTraceType
;
55 import org
.eclipse
.tracecompass
.tmf
.core
.project
.model
.TraceTypeHelper
;
56 import org
.eclipse
.tracecompass
.tmf
.core
.project
.model
.TraceValidationHelper
;
57 import org
.eclipse
.tracecompass
.tmf
.ui
.project
.model
.TmfTraceFolder
;
58 import org
.eclipse
.tracecompass
.tmf
.ui
.project
.model
.TmfTraceTypeUIUtils
;
59 import org
.eclipse
.ui
.IWorkbench
;
60 import org
.eclipse
.ui
.dialogs
.IOverwriteQuery
;
61 import org
.eclipse
.ui
.wizards
.datatransfer
.FileSystemStructureProvider
;
62 import org
.eclipse
.ui
.wizards
.datatransfer
.ImportOperation
;
65 * Batch Import trace wizard.
67 * @author Matthew Khouzam
69 public class BatchImportTraceWizard
extends ImportTraceWizard
{
71 private static final int WIN_HEIGHT
= 400;
72 private static final int WIN_WIDTH
= 800;
73 private static final Status CANCEL_STATUS
= new Status(IStatus
.CANCEL
, Activator
.PLUGIN_ID
, ""); //$NON-NLS-1$
74 private static final int TOTALWORK
= 65536;
79 private static final int MAX_FILES
= TOTALWORK
- 1;
80 private static final String BATCH_IMPORT_WIZARD
= "BatchImportTraceWizard"; //$NON-NLS-1$
86 private IWizardPage fSelectDirectoriesPage
;
87 private ImportTraceWizardScanPage fScanPage
;
88 private IWizardPage fSelectTypePage
;
89 private IWizardPage fOptions
;
91 private final List
<String
> fTraceTypesToScan
= new ArrayList
<>();
92 private final Set
<String
> fParentFilesToScan
= new HashSet
<>();
94 private ImportTraceContentProvider fScannedTraces
= new ImportTraceContentProvider(fTraceTypesToScan
, fParentFilesToScan
);
96 private final Map
<TraceValidationHelper
, Boolean
> fResults
= new HashMap
<>();
97 private boolean fOverwrite
= true;
98 private boolean fLinked
= true;
100 private BlockingQueue
<TraceValidationHelper
> fTracesToScan
;
101 private final Set
<FileAndName
> fTraces
= new TreeSet
<>();
103 private Map
<String
, Set
<String
>> fParentFiles
= new HashMap
<>();
105 // Target import directory (trace folder)
106 private IFolder fTargetFolder
;
109 * Returns the ScannedTraces model
111 * @return the ScannedTraces model
113 public ImportTraceContentProvider
getScannedTraces() {
114 return fScannedTraces
;
120 public BatchImportTraceWizard() {
121 IDialogSettings workbenchSettings
= Activator
.getDefault().getDialogSettings();
122 IDialogSettings section
= workbenchSettings
.getSection(BATCH_IMPORT_WIZARD
);
123 if (section
== null) {
124 section
= workbenchSettings
.addNewSection(BATCH_IMPORT_WIZARD
);
126 setDialogSettings(section
);
127 setNeedsProgressMonitor(true);
131 public void init(IWorkbench workbench
, IStructuredSelection selection
) {
133 fSelectDirectoriesPage
= new ImportTraceWizardSelectDirectoriesPage(workbench
, selection
);
134 fScanPage
= new ImportTraceWizardScanPage(workbench
, selection
);
135 fSelectTypePage
= new ImportTraceWizardSelectTraceTypePage(workbench
, selection
);
136 fOptions
= new ImportTraceWizardPageOptions(workbench
, selection
);
137 // keep in case it's called later
138 Iterator
<?
> iter
= selection
.iterator();
139 while (iter
.hasNext()) {
140 Object selected
= iter
.next();
141 if (selected
instanceof TmfTraceFolder
) {
142 fTargetFolder
= ((TmfTraceFolder
) selected
).getResource();
150 public void addPages() {
151 addPage(fSelectTypePage
);
152 addPage(fSelectDirectoriesPage
);
155 final WizardDialog container
= (WizardDialog
) getContainer();
156 if (container
!= null) {
157 container
.setPageSize(WIN_WIDTH
, WIN_HEIGHT
);
167 public void addFileToScan(final String fileName
) {
168 String absolutePath
= new File(fileName
).getAbsolutePath();
169 if (!fParentFiles
.containsKey(absolutePath
)) {
170 fParentFiles
.put(absolutePath
, new HashSet
<String
>());
171 startUpdateTask(Messages
.BatchImportTraceWizardAdd
+ ' ' + absolutePath
, absolutePath
);
178 * Remove files from selection
181 * the name of the file to remove
183 public void removeFile(final String fileName
) {
184 fParentFiles
.remove(fileName
);
185 fParentFilesToScan
.remove(fileName
);
186 startUpdateTask(Messages
.BatchImportTraceWizardRemove
+ ' ' + fileName
, null);
189 private void startUpdateTask(final String taskName
, final String fileAbsolutePath
) {
191 this.getContainer().run(true, true, new IRunnableWithProgress() {
194 public void run(IProgressMonitor monitor
) throws InvocationTargetException
, InterruptedException
{
195 synchronized (BatchImportTraceWizard
.this) { // this should
199 sm
= SubMonitor
.convert(monitor
);
200 sm
.setTaskName(taskName
);
201 sm
.setWorkRemaining(TOTALWORK
);
202 updateFiles(sm
, fileAbsolutePath
);
207 } catch (InvocationTargetException e
) {
208 Activator
.getDefault().logError(Messages
.ImportTraceWizardImportProblem
, e
);
209 } catch (InterruptedException e
) {
214 * The set of names of the selected files
216 * @return the set of names of the selected files
218 public Set
<String
> getFileNames() {
219 return fParentFilesToScan
;
223 * Reset the trace list to import
225 public void clearTraces() {
230 public boolean performFinish() {
231 if (fTraces
.isEmpty()) {
234 // if this turns out to be too slow, put in a progress monitor. Does not
235 // appear to be slow for the moment.
236 boolean success
= importTraces();
240 private boolean importTraces() {
241 boolean success
= false;
242 IOverwriteQuery overwriteQuery
= new IOverwriteQuery() {
244 public String
queryOverwrite(String file
) {
245 return fOverwrite ? IOverwriteQuery
.ALL
: IOverwriteQuery
.NO_ALL
;
248 FileSystemStructureProvider fileSystemStructureProvider
= FileSystemStructureProvider
.INSTANCE
;
250 for (FileAndName traceToImport
: fTraces
) {
253 if (TmfImportHelper
.createLink(fTargetFolder
, Path
.fromOSString(traceToImport
.getFile().getAbsolutePath()), traceToImport
.getName()) == null) {
257 success
= setTraceTypeAndSourceLocation(traceToImport
).isOK();
261 List
<File
> subList
= new ArrayList
<>();
262 IPath path
= fTargetFolder
.getFullPath();
263 File parentFile
= traceToImport
.getFile();
264 final boolean isFile
= parentFile
.isFile();
266 IFile resource
= ResourcesPlugin
.getWorkspace().getRoot().getFile(path
.append(traceToImport
.getName()));
267 if (fOverwrite
|| !resource
.exists()) {
268 subList
.add(parentFile
);
269 parentFile
= parentFile
.getParentFile();
270 try (final FileInputStream source
= new FileInputStream(traceToImport
.getFile());) {
271 if (resource
.exists()) {
272 resource
.delete(IResource
.FORCE
, new NullProgressMonitor());
274 resource
.create(source
, true, new NullProgressMonitor());
276 setTraceTypeAndSourceLocation(traceToImport
);
280 path
= path
.addTrailingSeparator().append(traceToImport
.getName());
281 // Add all files in trace directory
282 File
[] fileList
= traceToImport
.getFile().listFiles();
283 for (File child
: fileList
) {
287 Collections
.sort(subList
, new Comparator
<File
>() {
289 public int compare(File o1
, File o2
) {
290 return o1
.getAbsolutePath().compareTo(o2
.getAbsolutePath());
293 ImportOperation operation
= new ImportOperation(
296 fileSystemStructureProvider
,
299 operation
.setContext(getShell());
300 operation
.setCreateContainerStructure(false);
301 if (executeImportOperation(operation
)) {
302 setTraceTypeAndSourceLocation(traceToImport
);
308 } catch (Exception e
) {
314 private IStatus
setTraceTypeAndSourceLocation(FileAndName traceToImport
) {
315 IStatus status
= Status
.OK_STATUS
;
316 IResource resource
= fTargetFolder
.findMember(traceToImport
.getName());
317 if (resource
!= null) {
319 // Set the trace type for this resource
320 String traceTypeId
= traceToImport
.getTraceTypeId();
321 TraceTypeHelper traceType
= TmfTraceType
.getTraceType(traceTypeId
);
322 if (traceType
!= null) {
323 status
= TmfTraceTypeUIUtils
.setTraceType(resource
, traceType
);
326 // Set the source location for this resource
327 File file
= traceToImport
.getFile();
328 String sourceLocation
= null;
329 IResource sourceResource
;
330 if (file
.isDirectory()) {
331 sourceResource
= ResourcesPlugin
.getWorkspace().getRoot().getContainerForLocation(Path
.fromOSString(file
.getAbsolutePath()));
333 sourceResource
= ResourcesPlugin
.getWorkspace().getRoot().getFileForLocation(Path
.fromOSString(file
.getAbsolutePath()));
335 if (sourceResource
!= null && sourceResource
.exists()) {
336 sourceLocation
= sourceResource
.getPersistentProperty(TmfCommonConstants
.SOURCE_LOCATION
);
338 if (sourceLocation
== null) {
339 sourceLocation
= URIUtil
.toUnencodedString(file
.toURI());
341 resource
.setPersistentProperty(TmfCommonConstants
.SOURCE_LOCATION
, sourceLocation
);
342 } catch (CoreException e
) {
343 Activator
.getDefault().logError(Messages
.BatchImportTraceWizardErrorImportingTraceResource
344 + ' ' + resource
.getName(), e
);
351 public boolean canFinish() {
352 return super.canFinish() && hasTracesToImport() && !hasConflicts() && (fTargetFolder
!= null);
356 * Returns if a trace to import is selected
358 * @return if there are traces to import
360 public boolean hasTracesToImport() {
361 return fTraces
.size() > 0;
365 * Reset the files to scan
367 public void clearFilesToScan() {
368 fTracesToScan
.clear();
372 * Set the trace types to scan
374 * @param tracesToScan
375 * a list of trace types to scan for
377 public void setTraceTypesToScan(List
<String
> tracesToScan
) {
378 // intersection to know if there's a diff.
379 // if there's a diff, we need to re-enque everything
380 List
<String
> added
= new ArrayList
<>();
381 for (String traceLoc
: tracesToScan
) {
382 if (!fTraceTypesToScan
.contains(traceLoc
)) {
386 fTraceTypesToScan
.clear();
387 fTraceTypesToScan
.addAll(tracesToScan
);
388 updateTracesToScan(added
);
392 * Get the trace types to scan
394 * @return a list of traces to Scan for
396 public List
<String
> getTraceTypesToScan() {
397 return fTraceTypesToScan
;
401 * Add files to Import
404 * add the file and tracetype to import
406 public void addFileToImport(FileAndName element
) {
407 fTraces
.add(element
);
412 * Remove the file to scan
415 * the element to remove
417 public void removeFileToImport(FileAndName element
) {
418 fTraces
.remove(element
);
419 element
.setConflictingName(false);
424 * Updates the trace to see if there are conflicts.
426 public void updateConflicts() {
427 final FileAndName
[] fChildren
= fTraces
.toArray(new FileAndName
[0]);
428 for (int i
= 0; i
< fChildren
.length
; i
++) {
429 fChildren
[i
].setConflictingName(false);
431 for (int i
= 1; i
< fChildren
.length
; i
++) {
432 for (int j
= 0; j
< i
; j
++) {
433 if (fChildren
[i
].getName().equals(fChildren
[j
].getName())) {
434 fChildren
[i
].setConflictingName(true);
435 fChildren
[j
].setConflictingName(true);
439 getContainer().updateButtons();
443 * Is there a name conflict
445 boolean hasConflicts() {
446 boolean conflict
= false;
447 for (FileAndName child
: fTraces
) {
448 conflict
|= child
.isConflictingName();
453 private boolean executeImportOperation(ImportOperation op
) {
454 initializeOperation(op
);
457 getContainer().run(true, true, op
);
458 } catch (InterruptedException e
) {
460 } catch (InvocationTargetException e
) {
461 Activator
.getDefault().logError(Messages
.ImportTraceWizardImportProblem
, e
);
465 IStatus status
= op
.getStatus();
466 if (!status
.isOK()) {
467 ErrorDialog
.openError(getContainer().getShell(), Messages
.ImportTraceWizardImportProblem
, null, status
);
474 private static void initializeOperation(ImportOperation op
) {
475 op
.setCreateContainerStructure(false);
476 op
.setOverwriteResources(false);
477 op
.setVirtualFolders(false);
481 * Override existing resources
486 public void setOverwrite(boolean selection
) {
487 fOverwrite
= selection
;
491 * Is the trace linked?
496 public void setLinked(boolean isLink
) {
501 * @param tracesToScan
502 * sets the common traces to scan
504 public void setTracesToScan(BlockingQueue
<TraceValidationHelper
> tracesToScan
) {
505 fTracesToScan
= tracesToScan
;
511 * @return if the trace has been scanned yet or not
513 public boolean hasScanned(TraceValidationHelper traceToScan
) {
514 return fResults
.containsKey(traceToScan
);
518 * Add a result to a cache
521 * The trace that has been scanned
523 * if the trace is valid
525 public void addResult(TraceValidationHelper traceToScan
, boolean validate
) {
526 fResults
.put(traceToScan
, validate
);
530 * Gets if the trace has been scanned or not
534 * @return whether it passes or not
536 public boolean getResult(TraceValidationHelper traceToScan
) {
537 return fResults
.get(traceToScan
);
541 * Returns the amount of files scanned
543 * @return the amount of files scanned
545 public int getNumberOfResults() {
546 return fResults
.size();
549 private void updateTracesToScan(final List
<String
> added
) {
550 // Treeset is used instead of a hashset since the traces should be read
551 // in the order they were added.
552 final Set
<String
> filesToScan
= new TreeSet
<>();
553 for (String name
: fParentFiles
.keySet()) {
554 filesToScan
.addAll(fParentFiles
.get(name
));
556 IProgressMonitor pm
= new NullProgressMonitor();
558 updateScanQueue(pm
, filesToScan
, added
);
559 } catch (InterruptedException e
) {
565 * I am a job. Make me work
567 private synchronized IStatus
updateFiles(IProgressMonitor monitor
, String traceToScanAbsPath
) {
568 final Set
<String
> filesToScan
= new TreeSet
<>();
571 for (String name
: fParentFiles
.keySet()) {
573 final File file
= new File(name
);
574 final File
[] listFiles
= file
.listFiles();
575 if (listFiles
!= null) {
576 workToDo
+= listFiles
.length
;
579 int step
= TOTALWORK
/ workToDo
;
581 for (String name
: fParentFiles
.keySet()) {
582 final File fileToAdd
= new File(name
);
583 final Set
<String
> parentFilesToScan
= fParentFiles
.get(fileToAdd
.getAbsolutePath());
584 recurse(parentFilesToScan
, fileToAdd
, monitor
, step
);
585 if (monitor
.isCanceled()) {
586 fParentFilesToScan
.remove(traceToScanAbsPath
);
587 fParentFiles
.remove(traceToScanAbsPath
);
588 return CANCEL_STATUS
;
592 for (String name
: fParentFiles
.keySet()) {
593 filesToScan
.addAll(fParentFiles
.get(name
));
594 fParentFilesToScan
.add(name
);
596 IStatus cancelled
= updateScanQueue(monitor
, filesToScan
, fTraceTypesToScan
);
597 if (cancelled
.matches(IStatus
.CANCEL
)) {
598 fParentFilesToScan
.remove(traceToScanAbsPath
);
599 fParentFiles
.remove(traceToScanAbsPath
);
601 } catch (InterruptedException e
) {
603 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, e
.getMessage(), e
);
607 return Status
.OK_STATUS
;
610 private IStatus
updateScanQueue(IProgressMonitor monitor
, final Set
<String
> filesToScan
, final List
<String
> traceTypes
) throws InterruptedException
{
611 for (String fileToScan
: filesToScan
) {
612 for (String traceCat
: traceTypes
) {
613 TraceValidationHelper tv
= new TraceValidationHelper(fileToScan
, traceCat
);
614 // for thread safety, keep checks in this order.
615 if (!fResults
.containsKey(tv
)) {
616 if (!fTracesToScan
.contains(tv
)) {
617 fTracesToScan
.put(tv
);
618 monitor
.subTask(tv
.getTraceToScan());
619 if (monitor
.isCanceled()) {
621 return CANCEL_STATUS
;
628 return Status
.OK_STATUS
;
631 private IStatus
recurse(Set
<String
> filesToScan
, File fileToAdd
, IProgressMonitor monitor
, int step
) {
632 final String absolutePath
= fileToAdd
.getAbsolutePath();
633 if (!filesToScan
.contains(absolutePath
) && (filesToScan
.size() < MAX_FILES
)) {
634 filesToScan
.add(absolutePath
);
635 final File
[] listFiles
= fileToAdd
.listFiles();
636 if (null != listFiles
) {
637 for (File child
: listFiles
) {
638 monitor
.subTask(child
.getName());
639 if (monitor
.isCanceled()) {
640 return CANCEL_STATUS
;
642 IStatus retVal
= recurse(filesToScan
, child
, monitor
);
643 if (retVal
.matches(IStatus
.CANCEL
)) {
646 monitor
.worked(step
);
650 return Status
.OK_STATUS
;
653 private IStatus
recurse(Set
<String
> filesToScan
, File fileToAdd
, IProgressMonitor monitor
) {
654 final String absolutePath
= fileToAdd
.getAbsolutePath();
655 if (!filesToScan
.contains(absolutePath
) && (filesToScan
.size() < MAX_FILES
)) {
656 filesToScan
.add(absolutePath
);
657 final File
[] listFiles
= fileToAdd
.listFiles();
658 if (null != listFiles
) {
659 for (File child
: listFiles
) {
660 if (monitor
.isCanceled()) {
661 return CANCEL_STATUS
;
663 IStatus retVal
= recurse(filesToScan
, child
, monitor
);
664 if (retVal
.matches(IStatus
.CANCEL
)) {
670 return Status
.OK_STATUS
;
674 * Gets the folder in the resource (project)
676 * @param targetFolder
677 * the folder to import to
679 public void setTraceFolder(IFolder targetFolder
) {
680 fTargetFolder
= targetFolder
;
681 if (this.getContainer() != null && this.getContainer().getCurrentPage() != null) {
682 this.getContainer().updateButtons();
687 * Gets the target folder
689 * @return the target folder
691 public IFolder
getTargetFolder() {
692 return fTargetFolder
;