1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.ide.eclipse.traceview.editors; 17 18 import com.android.ide.eclipse.ddms.JavaSourceRevealer; 19 import com.android.ide.eclipse.traceview.TraceviewPlugin; 20 import com.android.traceview.ColorController; 21 import com.android.traceview.DmTraceReader; 22 import com.android.traceview.MethodData; 23 import com.android.traceview.ProfileView; 24 import com.android.traceview.ProfileView.MethodHandler; 25 import com.android.traceview.SelectionController; 26 import com.android.traceview.TimeLineView; 27 import com.android.traceview.TraceReader; 28 import com.android.traceview.TraceUnits; 29 30 import org.eclipse.core.filesystem.EFS; 31 import org.eclipse.core.filesystem.IFileStore; 32 import org.eclipse.core.filesystem.URIUtil; 33 import org.eclipse.core.resources.IFile; 34 import org.eclipse.core.resources.IWorkspace; 35 import org.eclipse.core.resources.IWorkspaceRoot; 36 import org.eclipse.core.resources.ResourcesPlugin; 37 import org.eclipse.core.runtime.CoreException; 38 import org.eclipse.core.runtime.IPath; 39 import org.eclipse.core.runtime.IProgressMonitor; 40 import org.eclipse.core.runtime.IStatus; 41 import org.eclipse.core.runtime.Status; 42 import org.eclipse.jface.dialogs.IDialogConstants; 43 import org.eclipse.jface.dialogs.IMessageProvider; 44 import org.eclipse.jface.dialogs.MessageDialog; 45 import org.eclipse.jface.window.Window; 46 import org.eclipse.swt.SWT; 47 import org.eclipse.swt.custom.SashForm; 48 import org.eclipse.swt.graphics.Color; 49 import org.eclipse.swt.layout.GridData; 50 import org.eclipse.swt.layout.GridLayout; 51 import org.eclipse.swt.widgets.Composite; 52 import org.eclipse.swt.widgets.Display; 53 import org.eclipse.swt.widgets.FileDialog; 54 import org.eclipse.swt.widgets.Label; 55 import org.eclipse.swt.widgets.Shell; 56 import org.eclipse.ui.IEditorInput; 57 import org.eclipse.ui.IEditorSite; 58 import org.eclipse.ui.PartInitException; 59 import org.eclipse.ui.dialogs.SaveAsDialog; 60 import org.eclipse.ui.ide.FileStoreEditorInput; 61 import org.eclipse.ui.part.EditorPart; 62 import org.eclipse.ui.part.FileEditorInput; 63 64 import java.io.File; 65 import java.io.IOException; 66 import java.net.URI; 67 68 public class TraceviewEditor extends EditorPart implements MethodHandler { 69 70 private Composite mParent; 71 private String mFilename; 72 private Composite mContents; 73 74 @Override doSave(IProgressMonitor monitor)75 public void doSave(IProgressMonitor monitor) { 76 // We do not modify the file 77 } 78 79 /* 80 * Copied from org.eclipse.ui.texteditor.AbstractDecoratedTextEditor. 81 */ 82 /** 83 * Checks whether there given file store points to a file in the workspace. 84 * Only returns a workspace file if there's a single match. 85 * 86 * @param fileStore the file store 87 * @return the <code>IFile</code> that matches the given file store 88 */ getWorkspaceFile(IFileStore fileStore)89 private IFile getWorkspaceFile(IFileStore fileStore) { 90 IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); 91 IFile[] files = workspaceRoot.findFilesForLocationURI(fileStore.toURI()); 92 if (files != null && files.length == 1) 93 return files[0]; 94 return null; 95 } 96 97 /* 98 * Based on the performSaveAs() method defined in class 99 * org.eclipse.ui.texteditor.AbstractDecoratedTextEditor of the 100 * org.eclipse.ui.editors plugin. 101 */ 102 @Override doSaveAs()103 public void doSaveAs() { 104 Shell shell = getSite().getShell(); 105 final IEditorInput input = getEditorInput(); 106 107 final IEditorInput newInput; 108 109 if (input instanceof FileEditorInput) { 110 // the file is part of the current workspace 111 FileEditorInput fileEditorInput = (FileEditorInput) input; 112 SaveAsDialog dialog = new SaveAsDialog(shell); 113 114 IFile original = fileEditorInput.getFile(); 115 if (original != null) { 116 dialog.setOriginalFile(original); 117 } 118 119 dialog.create(); 120 121 if (original != null && !original.isAccessible()) { 122 String message = String.format( 123 "The original file ''%s'' has been deleted or is not accessible.", 124 original.getName()); 125 dialog.setErrorMessage(null); 126 dialog.setMessage(message, IMessageProvider.WARNING); 127 } 128 129 if (dialog.open() == Window.CANCEL) { 130 return; 131 } 132 133 IPath filePath = dialog.getResult(); 134 if (filePath == null) { 135 return; 136 } 137 138 IWorkspace workspace = ResourcesPlugin.getWorkspace(); 139 IFile file = workspace.getRoot().getFile(filePath); 140 141 if (copy(shell, fileEditorInput.getURI(), file.getLocationURI()) == null) { 142 return; 143 } 144 145 try { 146 file.refreshLocal(IFile.DEPTH_ZERO, null); 147 } catch (CoreException e) { 148 // TODO Auto-generated catch block 149 e.printStackTrace(); 150 } 151 newInput = new FileEditorInput(file); 152 setInput(newInput); 153 setPartName(newInput.getName()); 154 } else if (input instanceof FileStoreEditorInput) { 155 // the file is not part of the current workspace 156 FileStoreEditorInput fileStoreEditorInput = (FileStoreEditorInput) input; 157 FileDialog dialog = new FileDialog(shell, SWT.SAVE); 158 IPath oldPath = URIUtil.toPath(fileStoreEditorInput.getURI()); 159 if (oldPath != null) { 160 dialog.setFileName(oldPath.lastSegment()); 161 dialog.setFilterPath(oldPath.toOSString()); 162 } 163 164 String path = dialog.open(); 165 if (path == null) { 166 return; 167 } 168 169 // Check whether file exists and if so, confirm overwrite 170 final File localFile = new File(path); 171 if (localFile.exists()) { 172 MessageDialog overwriteDialog = new MessageDialog( 173 shell, 174 "Save As", 175 null, 176 String.format( 177 "%s already exists.\nDo you want to replace it?" 178 , path), 179 MessageDialog.WARNING, 180 new String[] { 181 IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL 182 }, 1); // 'No' is the default 183 if (overwriteDialog.open() != Window.OK) { 184 return; 185 } 186 } 187 188 IFileStore destFileStore = copy(shell, fileStoreEditorInput.getURI(), localFile.toURI()); 189 if (destFileStore != null) { 190 IFile file = getWorkspaceFile(destFileStore); 191 if (file != null) { 192 newInput = new FileEditorInput(file); 193 } else { 194 newInput = new FileStoreEditorInput(destFileStore); 195 } 196 setInput(newInput); 197 setPartName(newInput.getName()); 198 } 199 } 200 } 201 copy(Shell shell, URI source, URI dest)202 private IFileStore copy(Shell shell, URI source, URI dest) { 203 IFileStore destFileStore = null; 204 IFileStore sourceFileStore = null; 205 try { 206 destFileStore = EFS.getStore(dest); 207 sourceFileStore = EFS.getStore(source); 208 sourceFileStore.copy(destFileStore, EFS.OVERWRITE, null); 209 } catch (CoreException ex) { 210 String title = "Problems During Save As..."; 211 String msg = String.format("Save could not be completed. %s", 212 ex.getMessage()); 213 MessageDialog.openError(shell, title, msg); 214 return null; 215 } 216 return destFileStore; 217 } 218 219 @Override init(IEditorSite site, IEditorInput input)220 public void init(IEditorSite site, IEditorInput input) throws PartInitException { 221 // The contract of init() mentions we need to fail if we can't 222 // understand the input. 223 if (input instanceof FileEditorInput) { 224 // We try to open a file that is part of the current workspace 225 FileEditorInput fileEditorInput = (FileEditorInput) input; 226 mFilename = fileEditorInput.getPath().toOSString(); 227 setSite(site); 228 setInput(input); 229 setPartName(input.getName()); 230 } else if (input instanceof FileStoreEditorInput) { 231 // We try to open a file that is not part of the current workspace 232 FileStoreEditorInput fileStoreEditorInput = (FileStoreEditorInput) input; 233 mFilename = fileStoreEditorInput.getURI().getPath(); 234 setSite(site); 235 setInput(input); 236 setPartName(input.getName()); 237 } else { 238 throw new PartInitException("Input is not of type FileEditorInput " + //$NON-NLS-1$ 239 "nor FileStoreEditorInput: " + //$NON-NLS-1$ 240 input == null ? "null" : input.toString()); //$NON-NLS-1$ 241 } 242 } 243 244 @Override isDirty()245 public boolean isDirty() { 246 return false; 247 } 248 249 @Override isSaveAsAllowed()250 public boolean isSaveAsAllowed() { 251 return true; 252 } 253 254 @Override createPartControl(Composite parent)255 public void createPartControl(Composite parent) { 256 mParent = parent; 257 try { 258 TraceReader reader = new DmTraceReader(mFilename, false); 259 reader.getTraceUnits().setTimeScale(TraceUnits.TimeScale.MilliSeconds); 260 261 mContents = new Composite(mParent, SWT.NONE); 262 263 Display display = mContents.getDisplay(); 264 ColorController.assignMethodColors(display, reader.getMethods()); 265 SelectionController selectionController = new SelectionController(); 266 267 GridLayout gridLayout = new GridLayout(1, false); 268 gridLayout.marginWidth = 0; 269 gridLayout.marginHeight = 0; 270 gridLayout.horizontalSpacing = 0; 271 gridLayout.verticalSpacing = 0; 272 mContents.setLayout(gridLayout); 273 274 Color darkGray = display.getSystemColor(SWT.COLOR_DARK_GRAY); 275 276 // Create a sash form to separate the timeline view (on top) 277 // and the profile view (on bottom) 278 SashForm sashForm1 = new SashForm(mContents, SWT.VERTICAL); 279 sashForm1.setBackground(darkGray); 280 sashForm1.SASH_WIDTH = 3; 281 GridData data = new GridData(GridData.FILL_BOTH); 282 sashForm1.setLayoutData(data); 283 284 // Create the timeline view 285 new TimeLineView(sashForm1, reader, selectionController); 286 287 // Create the profile view 288 new ProfileView(sashForm1, reader, selectionController).setMethodHandler(this); 289 } catch (IOException e) { 290 Label l = new Label(parent, 0); 291 l.setText("Failed to read the stack trace."); 292 293 Status status = new Status(IStatus.ERROR, TraceviewPlugin.PLUGIN_ID, 294 "Failed to read the stack trace.", e); 295 TraceviewPlugin.getDefault().getLog().log(status); 296 } 297 298 mParent.layout(); 299 } 300 301 @Override setFocus()302 public void setFocus() { 303 mParent.setFocus(); 304 } 305 306 // ---- MethodHandler methods 307 308 @Override handleMethod(MethodData method)309 public void handleMethod(MethodData method) { 310 String methodName = method.getMethodName(); 311 String className = method.getClassName().replaceAll("/", "."); //$NON-NLS-1$ //$NON-NLS-2$ 312 String fqmn = className + "." + methodName; //$NON-NLS-1$ 313 314 JavaSourceRevealer.revealMethod(fqmn, null, -1, null); 315 } 316 } 317