/******************************************************************************* * 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 * Marc-Andre Laperle - Log some exceptions * Patrick Tasse - Add support for source location *******************************************************************************/ package fr.inria.linuxtools.internal.tmf.ui.project.wizards.importtrace; import java.io.File; import java.io.FileInputStream; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.BlockingQueue; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.URIUtil; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.wizard.IWizardPage; import org.eclipse.jface.wizard.WizardDialog; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.dialogs.IOverwriteQuery; import org.eclipse.ui.wizards.datatransfer.FileSystemStructureProvider; import org.eclipse.ui.wizards.datatransfer.ImportOperation; import fr.inria.linuxtools.internal.tmf.ui.Activator; import fr.inria.linuxtools.internal.tmf.ui.project.model.TmfImportHelper; import fr.inria.linuxtools.tmf.core.TmfCommonConstants; import fr.inria.linuxtools.tmf.core.project.model.TmfTraceType; import fr.inria.linuxtools.tmf.core.project.model.TraceTypeHelper; import fr.inria.linuxtools.tmf.core.project.model.TraceValidationHelper; import fr.inria.linuxtools.tmf.ui.project.model.TmfTraceFolder; import fr.inria.linuxtools.tmf.ui.project.model.TmfTraceTypeUIUtils; /** * Batch Import trace wizard. * * @author Matthew Khouzam * @since 2.0 */ public class BatchImportTraceWizard extends ImportTraceWizard { private static final int WIN_HEIGHT = 400; private static final int WIN_WIDTH = 800; private static final Status CANCEL_STATUS = new Status(IStatus.CANCEL, Activator.PLUGIN_ID, ""); //$NON-NLS-1$ private static final int TOTALWORK = 65536; // ----------------- // Constants // ----------------- private static final int MAX_FILES = TOTALWORK - 1; private static final String BATCH_IMPORT_WIZARD = "BatchImportTraceWizard"; //$NON-NLS-1$ // ------------------ // Fields // ------------------ private IWizardPage fSelectDirectoriesPage; private ImportTraceWizardScanPage fScanPage; private IWizardPage fSelectTypePage; private IWizardPage fOptions; private final List<String> fTraceTypesToScan = new ArrayList<>(); private final Set<String> fParentFilesToScan = new HashSet<>(); private ImportTraceContentProvider fScannedTraces = new ImportTraceContentProvider(fTraceTypesToScan, fParentFilesToScan); private final Map<TraceValidationHelper, Boolean> fResults = new HashMap<>(); private boolean fOverwrite = true; private boolean fLinked = true; private BlockingQueue<TraceValidationHelper> fTracesToScan; private final Set<FileAndName> fTraces = new TreeSet<>(); private Map<String, Set<String>> fParentFiles = new HashMap<>(); // Target import directory (trace folder) private IFolder fTargetFolder; /** * Returns the ScannedTraces model * * @return the ScannedTraces model */ public ImportTraceContentProvider getScannedTraces() { return fScannedTraces; } /** * Constructor */ public BatchImportTraceWizard() { IDialogSettings workbenchSettings = Activator.getDefault().getDialogSettings(); IDialogSettings section = workbenchSettings.getSection(BATCH_IMPORT_WIZARD); if (section == null) { section = workbenchSettings.addNewSection(BATCH_IMPORT_WIZARD); } setDialogSettings(section); setNeedsProgressMonitor(true); } @Override public void init(IWorkbench workbench, IStructuredSelection selection) { fSelectDirectoriesPage = new ImportTraceWizardSelectDirectoriesPage(workbench, selection); fScanPage = new ImportTraceWizardScanPage(workbench, selection); fSelectTypePage = new ImportTraceWizardSelectTraceTypePage(workbench, selection); fOptions = new ImportTraceWizardPageOptions(workbench, selection); // keep in case it's called later Iterator<?> iter = selection.iterator(); while (iter.hasNext()) { Object selected = iter.next(); if (selected instanceof TmfTraceFolder) { fTargetFolder = ((TmfTraceFolder) selected).getResource(); break; } } fResults.clear(); } @Override public void addPages() { addPage(fSelectTypePage); addPage(fSelectDirectoriesPage); addPage(fScanPage); addPage(fOptions); final WizardDialog container = (WizardDialog) getContainer(); if (container != null) { container.setPageSize(WIN_WIDTH, WIN_HEIGHT); } } /** * Add a file to scan * * @param fileName * the file to scan */ public void addFileToScan(final String fileName) { String absolutePath = new File(fileName).getAbsolutePath(); if (!fParentFiles.containsKey(absolutePath)) { fParentFiles.put(absolutePath, new HashSet<String>()); startUpdateTask(Messages.BatchImportTraceWizardAdd + ' ' + absolutePath, absolutePath); } } /** * Remove files from selection * * @param fileName * the name of the file to remove */ public void removeFile(final String fileName) { fParentFiles.remove(fileName); fParentFilesToScan.remove(fileName); startUpdateTask(Messages.BatchImportTraceWizardRemove + ' ' + fileName, null); } private void startUpdateTask(final String taskName, final String fileAbsolutePath) { try { this.getContainer().run(true, true, new IRunnableWithProgress() { @Override public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { synchronized (BatchImportTraceWizard.this) { // this should // only run one // at a time SubMonitor sm; sm = SubMonitor.convert(monitor); sm.setTaskName(taskName); sm.setWorkRemaining(TOTALWORK); updateFiles(sm, fileAbsolutePath); sm.done(); } } }); } catch (InvocationTargetException e) { Activator.getDefault().logError(Messages.ImportTraceWizardImportProblem, e); } catch (InterruptedException e) { } } /** * The set of names of the selected files * * @return the set of names of the selected files */ public Set<String> getFileNames() { return fParentFilesToScan; } /** * Reset the trace list to import */ public void clearTraces() { fTraces.clear(); } @Override public boolean performFinish() { if (fTraces.isEmpty()) { return false; } // if this turns out to be too slow, put in a progress monitor. Does not // appear to be slow for the moment. boolean success = importTraces(); return success; } private boolean importTraces() { boolean success = false; IOverwriteQuery overwriteQuery = new IOverwriteQuery() { @Override public String queryOverwrite(String file) { return fOverwrite ? IOverwriteQuery.ALL : IOverwriteQuery.NO_ALL; } }; FileSystemStructureProvider fileSystemStructureProvider = FileSystemStructureProvider.INSTANCE; for (FileAndName traceToImport : fTraces) { try { if (fLinked) { if (TmfImportHelper.createLink(fTargetFolder, Path.fromOSString(traceToImport.getFile().getAbsolutePath()), traceToImport.getName()) == null) { success = false; } else { success = setTraceTypeAndSourceLocation(traceToImport).isOK(); } } else { List<File> subList = new ArrayList<>(); IPath path = fTargetFolder.getFullPath(); File parentFile = traceToImport.getFile(); final boolean isFile = parentFile.isFile(); if (isFile) { IFile resource = ResourcesPlugin.getWorkspace().getRoot().getFile(path.append(traceToImport.getName())); if (fOverwrite || !resource.exists()) { subList.add(parentFile); parentFile = parentFile.getParentFile(); try (final FileInputStream source = new FileInputStream(traceToImport.getFile());) { if (resource.exists()) { resource.delete(IResource.FORCE, new NullProgressMonitor()); } resource.create(source, true, new NullProgressMonitor()); } setTraceTypeAndSourceLocation(traceToImport); success = true; } } else { path = path.addTrailingSeparator().append(traceToImport.getName()); // Add all files in trace directory File[] fileList = traceToImport.getFile().listFiles(); for (File child : fileList) { subList.add(child); } Collections.sort(subList, new Comparator<File>() { @Override public int compare(File o1, File o2) { return o1.getAbsolutePath().compareTo(o2.getAbsolutePath()); } }); ImportOperation operation = new ImportOperation( path, parentFile, fileSystemStructureProvider, overwriteQuery, subList); operation.setContext(getShell()); operation.setCreateContainerStructure(false); if (executeImportOperation(operation)) { setTraceTypeAndSourceLocation(traceToImport); success = true; } } } } catch (Exception e) { } } return success; } private IStatus setTraceTypeAndSourceLocation(FileAndName traceToImport) { IStatus status = Status.OK_STATUS; IResource resource = fTargetFolder.findMember(traceToImport.getName()); if (resource != null) { try { // Set the trace type for this resource String traceTypeId = traceToImport.getTraceTypeId(); TraceTypeHelper traceType = TmfTraceType.getTraceType(traceTypeId); if (traceType != null) { status = TmfTraceTypeUIUtils.setTraceType(resource, traceType); } // Set the source location for this resource File file = traceToImport.getFile(); String sourceLocation = null; IResource sourceResource; if (file.isDirectory()) { sourceResource = ResourcesPlugin.getWorkspace().getRoot().getContainerForLocation(Path.fromOSString(file.getAbsolutePath())); } else { sourceResource = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(Path.fromOSString(file.getAbsolutePath())); } if (sourceResource != null && sourceResource.exists()) { sourceLocation = sourceResource.getPersistentProperty(TmfCommonConstants.SOURCE_LOCATION); } if (sourceLocation == null) { sourceLocation = URIUtil.toUnencodedString(file.toURI()); } resource.setPersistentProperty(TmfCommonConstants.SOURCE_LOCATION, sourceLocation); } catch (CoreException e) { Activator.getDefault().logError(Messages.BatchImportTraceWizardErrorImportingTraceResource + ' ' + resource.getName(), e); } } return status; } @Override public boolean canFinish() { return super.canFinish() && hasTracesToImport() && !hasConflicts() && (fTargetFolder != null); } /** * Returns if a trace to import is selected * * @return if there are traces to import */ public boolean hasTracesToImport() { return fTraces.size() > 0; } /** * Reset the files to scan */ public void clearFilesToScan() { fTracesToScan.clear(); } /** * Set the trace types to scan * * @param tracesToScan * a list of trace types to scan for */ public void setTraceTypesToScan(List<String> tracesToScan) { // intersection to know if there's a diff. // if there's a diff, we need to re-enque everything List<String> added = new ArrayList<>(); for (String traceLoc : tracesToScan) { if (!fTraceTypesToScan.contains(traceLoc)) { added.add(traceLoc); } } fTraceTypesToScan.clear(); fTraceTypesToScan.addAll(tracesToScan); updateTracesToScan(added); } /** * Get the trace types to scan * * @return a list of traces to Scan for */ public List<String> getTraceTypesToScan() { return fTraceTypesToScan; } /** * Add files to Import * * @param element * add the file and tracetype to import */ public void addFileToImport(FileAndName element) { fTraces.add(element); updateConflicts(); } /** * Remove the file to scan * * @param element * the element to remove */ public void removeFileToImport(FileAndName element) { fTraces.remove(element); element.setConflictingName(false); updateConflicts(); } /** * Updates the trace to see if there are conflicts. */ public void updateConflicts() { final FileAndName[] fChildren = fTraces.toArray(new FileAndName[0]); for (int i = 0; i < fChildren.length; i++) { fChildren[i].setConflictingName(false); } for (int i = 1; i < fChildren.length; i++) { for (int j = 0; j < i; j++) { if (fChildren[i].getName().equals(fChildren[j].getName())) { fChildren[i].setConflictingName(true); fChildren[j].setConflictingName(true); } } } getContainer().updateButtons(); } /** * Is there a name conflict */ boolean hasConflicts() { boolean conflict = false; for (FileAndName child : fTraces) { conflict |= child.isConflictingName(); } return conflict; } private boolean executeImportOperation(ImportOperation op) { initializeOperation(op); try { getContainer().run(true, true, op); } catch (InterruptedException e) { return false; } catch (InvocationTargetException e) { Activator.getDefault().logError(Messages.ImportTraceWizardImportProblem, e); return false; } IStatus status = op.getStatus(); if (!status.isOK()) { ErrorDialog.openError(getContainer().getShell(), Messages.ImportTraceWizardImportProblem, null, status); return false; } return true; } private static void initializeOperation(ImportOperation op) { op.setCreateContainerStructure(false); op.setOverwriteResources(false); op.setVirtualFolders(false); } /** * Override existing resources * * @param selection * true or false */ public void setOverwrite(boolean selection) { fOverwrite = selection; } /** * Is the trace linked? * * @param isLink * true or false */ public void setLinked(boolean isLink) { fLinked = isLink; } /** * @param tracesToScan * sets the common traces to scan */ public void setTracesToScan(BlockingQueue<TraceValidationHelper> tracesToScan) { fTracesToScan = tracesToScan; } /** * @param traceToScan * The trace to scan * @return if the trace has been scanned yet or not * @since 3.0 */ public boolean hasScanned(TraceValidationHelper traceToScan) { return fResults.containsKey(traceToScan); } /** * Add a result to a cache * * @param traceToScan * The trace that has been scanned * @param validate * if the trace is valid * @since 3.0 */ public void addResult(TraceValidationHelper traceToScan, boolean validate) { fResults.put(traceToScan, validate); } /** * Gets if the trace has been scanned or not * * @param traceToScan * the scanned trace * @return whether it passes or not * @since 3.0 */ public boolean getResult(TraceValidationHelper traceToScan) { return fResults.get(traceToScan); } /** * Returns the amount of files scanned * * @return the amount of files scanned */ public int getNumberOfResults() { return fResults.size(); } private void updateTracesToScan(final List<String> added) { // Treeset is used instead of a hashset since the traces should be read // in the order they were added. final Set<String> filesToScan = new TreeSet<>(); for (String name : fParentFiles.keySet()) { filesToScan.addAll(fParentFiles.get(name)); } IProgressMonitor pm = new NullProgressMonitor(); try { updateScanQueue(pm, filesToScan, added); } catch (InterruptedException e) { } } /* * I am a job. Make me work */ private synchronized IStatus updateFiles(IProgressMonitor monitor, String traceToScanAbsPath) { final Set<String> filesToScan = new TreeSet<>(); int workToDo = 1; for (String name : fParentFiles.keySet()) { final File file = new File(name); final File[] listFiles = file.listFiles(); if (listFiles != null) { workToDo += listFiles.length; } } int step = TOTALWORK / workToDo; try { for (String name : fParentFiles.keySet()) { final File fileToAdd = new File(name); final Set<String> parentFilesToScan = fParentFiles.get(fileToAdd.getAbsolutePath()); recurse(parentFilesToScan, fileToAdd, monitor, step); if (monitor.isCanceled()) { fParentFilesToScan.remove(traceToScanAbsPath); fParentFiles.remove(traceToScanAbsPath); return CANCEL_STATUS; } } filesToScan.clear(); for (String name : fParentFiles.keySet()) { filesToScan.addAll(fParentFiles.get(name)); fParentFilesToScan.add(name); } IStatus cancelled = updateScanQueue(monitor, filesToScan, fTraceTypesToScan); if (cancelled.matches(IStatus.CANCEL)) { fParentFilesToScan.remove(traceToScanAbsPath); fParentFiles.remove(traceToScanAbsPath); } } catch (InterruptedException e) { monitor.done(); return new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e); } monitor.done(); return Status.OK_STATUS; } private IStatus updateScanQueue(IProgressMonitor monitor, final Set<String> filesToScan, final List<String> traceTypes) throws InterruptedException { for (String fileToScan : filesToScan) { for (String traceCat : traceTypes) { TraceValidationHelper tv = new TraceValidationHelper(fileToScan, traceCat); // for thread safety, keep checks in this order. if (!fResults.containsKey(tv)) { if (!fTracesToScan.contains(tv)) { fTracesToScan.put(tv); monitor.subTask(tv.getTraceToScan()); if (monitor.isCanceled()) { fScanPage.refresh(); return CANCEL_STATUS; } } } } } fScanPage.refresh(); return Status.OK_STATUS; } private IStatus recurse(Set<String> filesToScan, File fileToAdd, IProgressMonitor monitor, int step) { final String absolutePath = fileToAdd.getAbsolutePath(); if (!filesToScan.contains(absolutePath) && (filesToScan.size() < MAX_FILES)) { filesToScan.add(absolutePath); final File[] listFiles = fileToAdd.listFiles(); if (null != listFiles) { for (File child : listFiles) { monitor.subTask(child.getName()); if (monitor.isCanceled()) { return CANCEL_STATUS; } IStatus retVal = recurse(filesToScan, child, monitor); if (retVal.matches(IStatus.CANCEL)) { return retVal; } monitor.worked(step); } } } return Status.OK_STATUS; } private IStatus recurse(Set<String> filesToScan, File fileToAdd, IProgressMonitor monitor) { final String absolutePath = fileToAdd.getAbsolutePath(); if (!filesToScan.contains(absolutePath) && (filesToScan.size() < MAX_FILES)) { filesToScan.add(absolutePath); final File[] listFiles = fileToAdd.listFiles(); if (null != listFiles) { for (File child : listFiles) { if (monitor.isCanceled()) { return CANCEL_STATUS; } IStatus retVal = recurse(filesToScan, child, monitor); if (retVal.matches(IStatus.CANCEL)) { return retVal; } } } } return Status.OK_STATUS; } /** * Gets the folder in the resource (project) * * @param targetFolder * the folder to import to */ public void setTraceFolder(IFolder targetFolder) { fTargetFolder = targetFolder; if (this.getContainer() != null && this.getContainer().getCurrentPage() != null) { this.getContainer().updateButtons(); } } /** * Gets the target folder * * @return the target folder */ public IFolder getTargetFolder() { return fTargetFolder; } }