| 1 | /******************************************************************************* |
| 2 | * Copyright (c) 2013 Ericsson |
| 3 | * |
| 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 |
| 8 | * |
| 9 | * Contributors: |
| 10 | * Marc-Andre Laperle - Initial API and implementation |
| 11 | *******************************************************************************/ |
| 12 | |
| 13 | package org.eclipse.linuxtools.internal.tmf.ui.project.wizards.tracepkg.importexport; |
| 14 | |
| 15 | import java.io.IOException; |
| 16 | import java.io.InputStream; |
| 17 | import java.lang.reflect.InvocationTargetException; |
| 18 | import java.text.MessageFormat; |
| 19 | import java.util.ArrayList; |
| 20 | import java.util.Enumeration; |
| 21 | import java.util.List; |
| 22 | import java.util.Map; |
| 23 | |
| 24 | import org.eclipse.core.resources.IFile; |
| 25 | import org.eclipse.core.resources.IMarker; |
| 26 | import org.eclipse.core.resources.IResource; |
| 27 | import org.eclipse.core.runtime.CoreException; |
| 28 | import org.eclipse.core.runtime.IPath; |
| 29 | import org.eclipse.core.runtime.IProgressMonitor; |
| 30 | import org.eclipse.core.runtime.IStatus; |
| 31 | import org.eclipse.core.runtime.Path; |
| 32 | import org.eclipse.core.runtime.Status; |
| 33 | import org.eclipse.core.runtime.SubProgressMonitor; |
| 34 | import org.eclipse.jface.operation.ModalContext; |
| 35 | import org.eclipse.linuxtools.internal.tmf.ui.Activator; |
| 36 | import org.eclipse.linuxtools.internal.tmf.ui.project.wizards.tracepkg.AbstractTracePackageOperation; |
| 37 | import org.eclipse.linuxtools.internal.tmf.ui.project.wizards.tracepkg.TracePackageBookmarkElement; |
| 38 | import org.eclipse.linuxtools.internal.tmf.ui.project.wizards.tracepkg.TracePackageElement; |
| 39 | import org.eclipse.linuxtools.internal.tmf.ui.project.wizards.tracepkg.TracePackageFilesElement; |
| 40 | import org.eclipse.linuxtools.internal.tmf.ui.project.wizards.tracepkg.TracePackageSupplFileElement; |
| 41 | import org.eclipse.linuxtools.internal.tmf.ui.project.wizards.tracepkg.TracePackageSupplFilesElement; |
| 42 | import org.eclipse.linuxtools.internal.tmf.ui.project.wizards.tracepkg.TracePackageTraceElement; |
| 43 | import org.eclipse.linuxtools.tmf.core.TmfCommonConstants; |
| 44 | import org.eclipse.linuxtools.tmf.ui.editors.TmfEventsEditor; |
| 45 | import org.eclipse.linuxtools.tmf.ui.project.model.TmfNavigatorContentProvider; |
| 46 | import org.eclipse.linuxtools.tmf.ui.project.model.TmfTraceElement; |
| 47 | import org.eclipse.linuxtools.tmf.ui.project.model.TmfTraceFolder; |
| 48 | import org.eclipse.linuxtools.tmf.ui.project.model.TmfTraceType; |
| 49 | import org.eclipse.linuxtools.tmf.ui.project.model.TraceTypeHelper; |
| 50 | import org.eclipse.ui.dialogs.IOverwriteQuery; |
| 51 | import org.eclipse.ui.ide.IDE; |
| 52 | import org.eclipse.ui.internal.wizards.datatransfer.TarException; |
| 53 | import org.eclipse.ui.wizards.datatransfer.IImportStructureProvider; |
| 54 | import org.eclipse.ui.wizards.datatransfer.ImportOperation; |
| 55 | |
| 56 | /** |
| 57 | * An operation that imports a trace package from an archive |
| 58 | * |
| 59 | * @author Marc-Andre Laperle |
| 60 | */ |
| 61 | @SuppressWarnings("restriction") |
| 62 | public class TracePackageImportOperation extends AbstractTracePackageOperation implements IOverwriteQuery { |
| 63 | |
| 64 | private final TracePackageTraceElement fImportTraceElement; |
| 65 | private final TmfTraceFolder fTmfTraceFolder; |
| 66 | |
| 67 | // Result of reading the manifest |
| 68 | private TracePackageElement fResultElement; |
| 69 | |
| 70 | /** |
| 71 | * Constructs a new import operation |
| 72 | * |
| 73 | * @param importTraceElement |
| 74 | * the trace element to be imported |
| 75 | * @param fileName |
| 76 | * the output file name |
| 77 | * @param tmfTraceFolder |
| 78 | * the destination folder |
| 79 | */ |
| 80 | public TracePackageImportOperation(String fileName, TracePackageTraceElement importTraceElement, TmfTraceFolder tmfTraceFolder) { |
| 81 | super(fileName); |
| 82 | fImportTraceElement = importTraceElement; |
| 83 | fTmfTraceFolder = tmfTraceFolder; |
| 84 | } |
| 85 | |
| 86 | private class ImportProvider implements IImportStructureProvider { |
| 87 | |
| 88 | private Exception fException; |
| 89 | |
| 90 | @Override |
| 91 | public List getChildren(Object element) { |
| 92 | return null; |
| 93 | } |
| 94 | |
| 95 | @Override |
| 96 | public InputStream getContents(Object element) { |
| 97 | InputStream inputStream = null; |
| 98 | // We can add throws |
| 99 | try { |
| 100 | inputStream = ((ArchiveProviderElement) element).getContents(); |
| 101 | } catch (IOException e) { |
| 102 | fException = e; |
| 103 | } catch (TarException e) { |
| 104 | fException = e; |
| 105 | } |
| 106 | return inputStream; |
| 107 | } |
| 108 | |
| 109 | @Override |
| 110 | public String getFullPath(Object element) { |
| 111 | return ((ArchiveProviderElement) element).getFullPath(); |
| 112 | } |
| 113 | |
| 114 | @Override |
| 115 | public String getLabel(Object element) { |
| 116 | return ((ArchiveProviderElement) element).getLabel(); |
| 117 | } |
| 118 | |
| 119 | @Override |
| 120 | public boolean isFolder(Object element) { |
| 121 | return ((ArchiveProviderElement) element).isFolder(); |
| 122 | } |
| 123 | |
| 124 | public Exception getException() { |
| 125 | return fException; |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | private class ArchiveProviderElement { |
| 130 | |
| 131 | private final String fPath; |
| 132 | private final String fLabel; |
| 133 | |
| 134 | private ArchiveFile fArchiveFile; |
| 135 | private ArchiveEntry fEntry; |
| 136 | |
| 137 | public ArchiveProviderElement(String destinationPath, String label, ArchiveFile archiveFile, ArchiveEntry entry) { |
| 138 | fPath = destinationPath; |
| 139 | fLabel = label; |
| 140 | this.fArchiveFile = archiveFile; |
| 141 | this.fEntry = entry; |
| 142 | } |
| 143 | |
| 144 | public InputStream getContents() throws TarException, IOException { |
| 145 | return fArchiveFile.getInputStream(fEntry); |
| 146 | } |
| 147 | |
| 148 | public String getFullPath() { |
| 149 | return fPath; |
| 150 | } |
| 151 | |
| 152 | public String getLabel() { |
| 153 | return fLabel; |
| 154 | } |
| 155 | |
| 156 | public boolean isFolder() { |
| 157 | return false; |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | /** |
| 162 | * Run the operation. The status (result) of the operation can be obtained |
| 163 | * with {@link #getStatus} |
| 164 | * |
| 165 | * @param progressMonitor |
| 166 | * the progress monitor to use to display progress and receive |
| 167 | * requests for cancellation |
| 168 | */ |
| 169 | @Override |
| 170 | public void run(IProgressMonitor progressMonitor) { |
| 171 | int totalWork = getNbCheckedElements(new TracePackageElement[] { fImportTraceElement }) * 2; |
| 172 | progressMonitor.beginTask(Messages.TracePackageImportOperation_ImportingPackage, totalWork); |
| 173 | doRun(progressMonitor); |
| 174 | progressMonitor.done(); |
| 175 | } |
| 176 | |
| 177 | private void doRun(IProgressMonitor progressMonitor) { |
| 178 | try { |
| 179 | setStatus(deleteExistingTrace(progressMonitor)); |
| 180 | if (getStatus().getSeverity() != IStatus.OK) { |
| 181 | return; |
| 182 | } |
| 183 | |
| 184 | TracePackageElement[] children = fImportTraceElement.getChildren(); |
| 185 | for (TracePackageElement element : children) { |
| 186 | ModalContext.checkCanceled(progressMonitor); |
| 187 | |
| 188 | if (element instanceof TracePackageFilesElement) { |
| 189 | TracePackageFilesElement traceFilesElement = (TracePackageFilesElement) element; |
| 190 | setStatus(importTraceFiles(progressMonitor, traceFilesElement)); |
| 191 | |
| 192 | } else if (element instanceof TracePackageSupplFilesElement) { |
| 193 | TracePackageSupplFilesElement suppFilesElement = (TracePackageSupplFilesElement) element; |
| 194 | setStatus(importSupplFiles(progressMonitor, suppFilesElement)); |
| 195 | } |
| 196 | |
| 197 | if (getStatus().getSeverity() != IStatus.OK) { |
| 198 | return; |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | String traceName = fImportTraceElement.getText(); |
| 203 | IResource traceRes = fTmfTraceFolder.getResource().findMember(traceName); |
| 204 | if (traceRes == null || !traceRes.exists()) { |
| 205 | setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, MessageFormat.format(Messages.ImportTracePackageWizardPage_ErrorFindingImportedTrace, traceName))); |
| 206 | return; |
| 207 | } |
| 208 | |
| 209 | TraceTypeHelper traceType = TmfTraceType.getInstance().getTraceType(fImportTraceElement.getTraceType()); |
| 210 | if (traceType == null) { |
| 211 | setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, MessageFormat.format(Messages.ImportTracePackageWizardPage_ErrorSettingTraceType, fImportTraceElement.getTraceType(), traceName))); |
| 212 | return; |
| 213 | } |
| 214 | |
| 215 | try { |
| 216 | TmfTraceType.setTraceType(traceRes.getFullPath(), traceType); |
| 217 | } catch (CoreException e) { |
| 218 | setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, MessageFormat.format(Messages.ImportTracePackageWizardPage_ErrorSettingTraceType, fImportTraceElement.getTraceType(), traceName), e)); |
| 219 | } |
| 220 | |
| 221 | importBookmarks(traceRes, progressMonitor); |
| 222 | |
| 223 | } catch (InterruptedException e) { |
| 224 | setStatus(Status.CANCEL_STATUS); |
| 225 | } |
| 226 | } |
| 227 | |
| 228 | private IStatus deleteExistingTrace(IProgressMonitor progressMonitor) { |
| 229 | List<TmfTraceElement> traces = fTmfTraceFolder.getTraces(); |
| 230 | TmfTraceElement existingTrace = null; |
| 231 | |
| 232 | for (TmfTraceElement t : traces) { |
| 233 | if (t.getName().equals(fImportTraceElement.getText())) { |
| 234 | existingTrace = t; |
| 235 | break; |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | if (existingTrace != null) { |
| 240 | try { |
| 241 | existingTrace.delete(new SubProgressMonitor(progressMonitor, 1, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK)); |
| 242 | } catch (CoreException e) { |
| 243 | return new Status(IStatus.ERROR, Activator.PLUGIN_ID, org.eclipse.linuxtools.internal.tmf.ui.project.wizards.tracepkg.Messages.TracePackage_ErrorOperation, e); |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | return Status.OK_STATUS; |
| 248 | } |
| 249 | |
| 250 | private void importBookmarks(IResource traceRes, IProgressMonitor monitor) { |
| 251 | for (TracePackageElement o : fImportTraceElement.getChildren()) { |
| 252 | if (o instanceof TracePackageBookmarkElement && o.isChecked()) { |
| 253 | |
| 254 | // Get element |
| 255 | IFile bookmarksFile = null; |
| 256 | List<TmfTraceElement> traces = fTmfTraceFolder.getTraces(); |
| 257 | for (TmfTraceElement t : traces) { |
| 258 | if (t.getName().equals(traceRes.getName())) { |
| 259 | try { |
| 260 | bookmarksFile = t.createBookmarksFile(); |
| 261 | |
| 262 | // Make sure that if a bookmark is double-clicked first |
| 263 | // before opening the trace, it opens the right editor |
| 264 | |
| 265 | // Get the editor id from the extension point |
| 266 | String traceEditorId = t.getEditorId(); |
| 267 | final String editorId = (traceEditorId != null) ? traceEditorId : TmfEventsEditor.ID; |
| 268 | IDE.setDefaultEditor(bookmarksFile, editorId); |
| 269 | |
| 270 | } catch (CoreException e) { |
| 271 | Activator.getDefault().logError(MessageFormat.format(Messages.TracePackageImportOperation_ErrorCreatingBookmarkFile, traceRes.getName()), e); |
| 272 | } |
| 273 | break; |
| 274 | } |
| 275 | } |
| 276 | |
| 277 | if (bookmarksFile == null) { |
| 278 | break; |
| 279 | } |
| 280 | |
| 281 | TracePackageBookmarkElement bookmarkElement = (TracePackageBookmarkElement) o; |
| 282 | |
| 283 | List<Map<String, String>> bookmarks = bookmarkElement.getBookmarks(); |
| 284 | for (Map<String, String> attrs : bookmarks) { |
| 285 | IMarker createMarker = null; |
| 286 | try { |
| 287 | createMarker = bookmarksFile.createMarker(IMarker.BOOKMARK); |
| 288 | } catch (CoreException e) { |
| 289 | Activator.getDefault().logError(MessageFormat.format(Messages.TracePackageImportOperation_ErrorCreatingBookmark, traceRes.getName()), e); |
| 290 | } |
| 291 | if (createMarker != null && createMarker.exists()) { |
| 292 | try { |
| 293 | for (String key : attrs.keySet()) { |
| 294 | String value = attrs.get(key); |
| 295 | if (key.equals(IMarker.LOCATION)) { |
| 296 | createMarker.setAttribute(IMarker.LOCATION, Integer.valueOf(value).intValue()); |
| 297 | } else { |
| 298 | createMarker.setAttribute(key, value); |
| 299 | } |
| 300 | } |
| 301 | } catch (CoreException e) { |
| 302 | Activator.getDefault().logError(MessageFormat.format(Messages.TracePackageImportOperation_ErrorCreatingBookmark, traceRes.getName()), e); |
| 303 | } |
| 304 | } |
| 305 | } |
| 306 | } |
| 307 | } |
| 308 | |
| 309 | monitor.worked(1); |
| 310 | } |
| 311 | |
| 312 | private static boolean fileNameMatches(String fileName, String entryName) { |
| 313 | boolean fileMatch = entryName.equalsIgnoreCase(fileName); |
| 314 | boolean folderMatch = entryName.startsWith(fileName + "/"); //$NON-NLS-1$ |
| 315 | return fileMatch || folderMatch; |
| 316 | } |
| 317 | |
| 318 | private IStatus importTraceFiles(IProgressMonitor monitor, TracePackageFilesElement traceFilesElement) { |
| 319 | List<String> fileNames = new ArrayList<String>(); |
| 320 | IPath prefix = new Path(TmfTraceFolder.TRACE_FOLDER_NAME); |
| 321 | fileNames.add(traceFilesElement.getFileName()); |
| 322 | IPath containerPath = fTmfTraceFolder.getPath(); |
| 323 | IStatus status = importFiles(getSpecifiedArchiveFile(), fileNames, prefix, containerPath, monitor); |
| 324 | if (status.isOK()) { |
| 325 | new TmfNavigatorContentProvider().getChildren(fTmfTraceFolder); |
| 326 | } |
| 327 | return status; |
| 328 | } |
| 329 | |
| 330 | private IStatus importSupplFiles(IProgressMonitor monitor, TracePackageSupplFilesElement suppFilesElement) { |
| 331 | List<String> fileNames = new ArrayList<String>(); |
| 332 | for (TracePackageElement child : suppFilesElement.getChildren()) { |
| 333 | TracePackageSupplFileElement supplFile = (TracePackageSupplFileElement) child; |
| 334 | fileNames.add(supplFile.getText()); |
| 335 | } |
| 336 | |
| 337 | if (!fileNames.isEmpty()) { |
| 338 | List<TmfTraceElement> traces = fTmfTraceFolder.getTraces(); |
| 339 | TmfTraceElement traceElement = null; |
| 340 | for (TmfTraceElement t : traces) { |
| 341 | if (t.getName().equals(fImportTraceElement.getText())) { |
| 342 | traceElement = t; |
| 343 | break; |
| 344 | } |
| 345 | } |
| 346 | |
| 347 | if (traceElement != null) { |
| 348 | ArchiveFile archiveFile = getSpecifiedArchiveFile(); |
| 349 | traceElement.refreshSupplementaryFolder(); |
| 350 | String traceName = traceElement.getResource().getName(); |
| 351 | // Project/.tracing/tracename |
| 352 | IPath destinationContainerPath = traceElement.getTraceSupplementaryFolder(traceName).getFullPath(); |
| 353 | // .tracing/tracename |
| 354 | IPath pathInArchive = new Path(TmfCommonConstants.TRACE_SUPPLEMENATARY_FOLDER_NAME).append(traceName); |
| 355 | return importFiles(archiveFile, fileNames, pathInArchive, destinationContainerPath, monitor); |
| 356 | } |
| 357 | } |
| 358 | |
| 359 | return Status.OK_STATUS; |
| 360 | } |
| 361 | |
| 362 | private IStatus importFiles(ArchiveFile archiveFile, List<String> fileNames, IPath pathInArchive, IPath destinationContainerPath, IProgressMonitor monitor) { |
| 363 | List<ArchiveProviderElement> objects = new ArrayList<ArchiveProviderElement>(); |
| 364 | Enumeration<?> entries = archiveFile.entries(); |
| 365 | while (entries.hasMoreElements()) { |
| 366 | ArchiveEntry entry = (ArchiveEntry) entries.nextElement(); |
| 367 | String entryName = entry.getName(); |
| 368 | IPath fullArchivePath = new Path(entryName); |
| 369 | if (fullArchivePath.hasTrailingSeparator()) { |
| 370 | // We only care about file entries as the folders will get created by the ImportOperation |
| 371 | continue; |
| 372 | } |
| 373 | |
| 374 | for (String fileName : fileNames) { |
| 375 | // Check if this archive entry matches the searched file name at this archive location |
| 376 | IPath searchedArchivePath = pathInArchive.append(fileName); |
| 377 | if (fileNameMatches(searchedArchivePath.toString(), entryName)) { |
| 378 | // Traces/kernel/metadata |
| 379 | // kernel/metadata, the ImportOperation will take care of creating the kernel folder |
| 380 | IPath destinationPath = fullArchivePath.removeFirstSegments(pathInArchive.segmentCount()); |
| 381 | // metadata |
| 382 | String resourceLabel = fullArchivePath.lastSegment(); |
| 383 | |
| 384 | ArchiveProviderElement pe = new ArchiveProviderElement(destinationPath.toString(), resourceLabel, archiveFile, entry); |
| 385 | objects.add(pe); |
| 386 | break; |
| 387 | } |
| 388 | } |
| 389 | } |
| 390 | |
| 391 | ImportProvider provider = new ImportProvider(); |
| 392 | |
| 393 | ImportOperation operation = new ImportOperation(destinationContainerPath, |
| 394 | null, provider, this, |
| 395 | objects); |
| 396 | operation.setCreateContainerStructure(true); |
| 397 | operation.setOverwriteResources(true); |
| 398 | |
| 399 | try { |
| 400 | operation.run(new SubProgressMonitor(monitor, fileNames.size(), SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK)); |
| 401 | archiveFile.close(); |
| 402 | } catch (InvocationTargetException e) { |
| 403 | return new Status(IStatus.ERROR, Activator.PLUGIN_ID, org.eclipse.linuxtools.internal.tmf.ui.project.wizards.tracepkg.Messages.TracePackage_ErrorOperation, e); |
| 404 | } catch (InterruptedException e) { |
| 405 | return Status.CANCEL_STATUS; |
| 406 | } catch (IOException e) { |
| 407 | return new Status(IStatus.ERROR, Activator.PLUGIN_ID, org.eclipse.linuxtools.internal.tmf.ui.project.wizards.tracepkg.Messages.TracePackage_ErrorOperation, e); |
| 408 | } |
| 409 | |
| 410 | if (provider.getException() != null) { |
| 411 | return new Status(IStatus.ERROR, Activator.PLUGIN_ID, org.eclipse.linuxtools.internal.tmf.ui.project.wizards.tracepkg.Messages.TracePackage_ErrorOperation, provider.getException()); |
| 412 | } |
| 413 | |
| 414 | return operation.getStatus(); |
| 415 | } |
| 416 | |
| 417 | @Override |
| 418 | public String queryOverwrite(String pathString) { |
| 419 | // We always overwrite once we reach this point |
| 420 | return null; |
| 421 | } |
| 422 | |
| 423 | /** |
| 424 | * Get the resulting element from extracting the manifest from the archive |
| 425 | * |
| 426 | * @return the resulting element |
| 427 | */ |
| 428 | public TracePackageElement getResultElement() { |
| 429 | return fResultElement; |
| 430 | } |
| 431 | |
| 432 | } |