2 * Copyright 2000-2014 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.byteCodeViewer;
18 import com.intellij.codeInsight.documentation.DocumentationManager;
19 import com.intellij.codeInsight.lookup.LookupManager;
20 import com.intellij.icons.AllIcons;
21 import com.intellij.openapi.actionSystem.AnAction;
22 import com.intellij.openapi.actionSystem.AnActionEvent;
23 import com.intellij.openapi.actionSystem.CommonDataKeys;
24 import com.intellij.openapi.actionSystem.DataContext;
25 import com.intellij.openapi.application.ApplicationManager;
26 import com.intellij.openapi.compiler.CompilerManager;
27 import com.intellij.openapi.editor.Editor;
28 import com.intellij.openapi.progress.ProgressIndicator;
29 import com.intellij.openapi.progress.ProgressManager;
30 import com.intellij.openapi.progress.Task;
31 import com.intellij.openapi.project.Project;
32 import com.intellij.openapi.roots.ProjectRootManager;
33 import com.intellij.openapi.ui.Messages;
34 import com.intellij.openapi.ui.popup.JBPopup;
35 import com.intellij.openapi.ui.popup.JBPopupFactory;
36 import com.intellij.openapi.util.Computable;
37 import com.intellij.openapi.util.Disposer;
38 import com.intellij.openapi.vfs.VirtualFile;
39 import com.intellij.psi.*;
40 import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
41 import com.intellij.psi.util.PsiUtilBase;
42 import com.intellij.psi.util.PsiUtilCore;
43 import com.intellij.ui.awt.RelativePoint;
44 import com.intellij.util.Processor;
45 import org.jetbrains.annotations.NotNull;
46 import org.jetbrains.annotations.Nullable;
52 public class ShowByteCodeAction extends AnAction {
54 public void update(AnActionEvent e) {
55 e.getPresentation().setEnabled(false);
56 e.getPresentation().setIcon(AllIcons.Toolwindows.Documentation);
57 final Project project = e.getData(CommonDataKeys.PROJECT);
58 if (project != null) {
59 final PsiElement psiElement = getPsiElement(e.getDataContext(), project, e.getData(CommonDataKeys.EDITOR));
60 if (psiElement != null) {
61 if (psiElement.getContainingFile() instanceof PsiClassOwner &&
62 ByteCodeViewerManager.getContainingClass(psiElement) != null) {
63 e.getPresentation().setEnabled(true);
70 public void actionPerformed(AnActionEvent e) {
71 final DataContext dataContext = e.getDataContext();
72 final Project project = CommonDataKeys.PROJECT.getData(dataContext);
73 if (project == null) return;
74 final Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
76 final PsiElement psiElement = getPsiElement(dataContext, project, editor);
77 if (psiElement == null) return;
79 final String psiElementTitle = ByteCodeViewerManager.getInstance(project).getTitle(psiElement);
81 final VirtualFile virtualFile = PsiUtilCore.getVirtualFile(psiElement);
82 if (virtualFile == null) return;
84 final RelativePoint bestPopupLocation = JBPopupFactory.getInstance().guessBestPopupLocation(dataContext);
86 final SmartPsiElementPointer element = SmartPointerManager.getInstance(project).createSmartPsiElementPointer(psiElement);
87 ProgressManager.getInstance().run(new Task.Backgroundable(project, "Looking for bytecode...") {
88 private String myByteCode;
89 private String myErrorMessage;
90 private String myErrorTitle;
93 public void run(@NotNull ProgressIndicator indicator) {
94 if (ProjectRootManager.getInstance(project).getFileIndex().isInContent(virtualFile) && isMarkedForCompilation(project, virtualFile)) {
95 myErrorMessage = "Unable to show bytecode for '" + psiElementTitle + "'. Class file does not exist or is out-of-date.";
96 myErrorTitle = "Class File Out-Of-Date";
99 myByteCode = ApplicationManager.getApplication().runReadAction(new Computable<String>() {
101 public String compute() {
102 return ByteCodeViewerManager.getByteCode(psiElement);
109 public void onSuccess() {
110 if (project.isDisposed()) return;
112 if (myErrorMessage != null && myTitle != null) {
113 Messages.showWarningDialog(project, myErrorMessage, myErrorTitle);
116 final PsiElement targetElement = element.getElement();
117 if (targetElement == null) return;
119 final ByteCodeViewerManager codeViewerManager = ByteCodeViewerManager.getInstance(project);
120 if (codeViewerManager.hasActiveDockedDocWindow()) {
121 codeViewerManager.doUpdateComponent(targetElement, myByteCode);
124 if (myByteCode == null) {
125 Messages.showErrorDialog(project, "Unable to parse class file for '" + psiElementTitle + "'.", "Bytecode not Found");
128 final ByteCodeViewerComponent component = new ByteCodeViewerComponent(project, null);
129 component.setText(myByteCode, targetElement);
130 Processor<JBPopup> pinCallback = new Processor<JBPopup>() {
132 public boolean process(JBPopup popup) {
133 codeViewerManager.recreateToolWindow(targetElement, targetElement);
139 final JBPopup popup = JBPopupFactory.getInstance().createComponentPopupBuilder(component, null)
141 .setDimensionServiceKey(project, DocumentationManager.JAVADOC_LOCATION_AND_SIZE, false)
144 .setRequestFocus(LookupManager.getActiveLookup(editor) == null)
145 .setTitle(psiElementTitle + " Bytecode")
146 .setCouldPin(pinCallback)
148 Disposer.register(popup, component);
150 if (editor != null) {
151 popup.showInBestPositionFor(editor);
153 popup.show(bestPopupLocation);
160 private static boolean isMarkedForCompilation(Project project, VirtualFile virtualFile) {
161 final CompilerManager compilerManager = CompilerManager.getInstance(project);
162 return !compilerManager.isUpToDate(compilerManager.createFilesCompileScope(new VirtualFile[]{virtualFile}));
166 private static PsiElement getPsiElement(DataContext dataContext, Project project, Editor editor) {
167 PsiElement psiElement = null;
168 if (editor == null) {
169 psiElement = CommonDataKeys.PSI_ELEMENT.getData(dataContext);
171 final PsiFile file = PsiUtilBase.getPsiFileInEditor(editor, project);
172 final Editor injectedEditor = InjectedLanguageUtil.getEditorForInjectedLanguageNoCommit(editor, file);
173 if (injectedEditor != null) {
174 PsiFile psiFile = PsiUtilBase.getPsiFileInEditor(injectedEditor, project);
175 psiElement = psiFile != null ? psiFile.findElementAt(injectedEditor.getCaretModel().getOffset()) : null;
178 if (file != null && psiElement == null) {
179 psiElement = file.findElementAt(editor.getCaretModel().getOffset());