KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > de > loskutov > bco > editors > BytecodeDocumentProvider


1 package de.loskutov.bco.editors;
2
3 import org.eclipse.core.runtime.CoreException;
4 import org.eclipse.core.runtime.IStatus;
5 import org.eclipse.jdt.core.IBuffer;
6 import org.eclipse.jdt.core.IClassFile;
7 import org.eclipse.jdt.internal.core.BufferManager;
8 import org.eclipse.jdt.internal.ui.javaeditor.ClassFileDocumentProvider;
9 import org.eclipse.jdt.internal.ui.javaeditor.IClassFileEditorInput;
10 import org.eclipse.jface.text.AbstractDocument;
11 import org.eclipse.jface.text.BadLocationException;
12 import org.eclipse.jface.text.IDocument;
13 import org.eclipse.jface.text.IRegion;
14 import org.eclipse.ui.IEditorInput;
15 import org.eclipse.ui.IEditorPart;
16 import org.eclipse.ui.PlatformUI;
17
18 import de.loskutov.bco.BytecodeOutlinePlugin;
19
20 /**
21  * Overriden to get control over document content for bytecode editors
22  * @author Andrei
23  */

24 public class BytecodeDocumentProvider extends ClassFileDocumentProvider {
25
26     public BytecodeDocumentProvider() {
27         super();
28     }
29
30
31     /*
32      * Overriden to get control over document content for bytecode editors
33      * @see StorageDocumentProvider#setDocumentContent(IDocument, IEditorInput)
34      */

35     protected boolean setDocumentContent(IDocument document,
36         IEditorInput editorInput, String JavaDoc encoding) throws CoreException {
37
38         if (editorInput instanceof IClassFileEditorInput) {
39             IClassFile classFile = ((IClassFileEditorInput) editorInput)
40                 .getClassFile();
41
42             String JavaDoc source = classFile.getSource();
43             if (source == null) {
44                 // this could be the case for class files which are not on the class path
45
// buffer should be already opened and created in our editor->doOpenBuffer
46
// method
47
IBuffer buffer = BufferManager.getDefaultBufferManager().getBuffer(classFile);
48                 if (buffer != null) {
49                     source = buffer.getContents();
50                 }
51             }
52             document.set(source);
53             return true;
54         }
55         return super.setDocumentContent(document, editorInput, encoding);
56     }
57
58     /**
59      *
60      * During debug session, debugger tries to get line information for the current line
61      * in the stack, and then uses this info to set cursor and select text in editor.
62      *
63      * The problem is, that Java debugger knows only "source" - based lines, but our editor
64      * contains "bytecode" text, where the lines are NOT aligned to the source code lines.
65      * (it is simply not possible).
66      *
67      * So if we do not change the default implementation, the selected bytecode text will
68      * never match requested sourcecode lines.
69      *
70      * As workaround we "just" pass to the debugger another document, as one we use to
71      * represent the text in our editor. This document is a proxy and just replaces
72      * the implementation of "getLineInformation" method. Our implementation mapps the
73      * requested sourcecode line to the bytecode line in editor.
74      *
75      * All other clients of this method shouldn't be affected and should receive always
76      * the original document.
77      */

78     public IDocument getDocument(Object JavaDoc element) {
79         IDocument document = super.getDocument(element);
80         if (element instanceof IClassFileEditorInput && isDebuggerCall()) {
81             IClassFileEditorInput input = (IClassFileEditorInput) element;
82
83             return new DocumentProxy4Debugger(document, input.getClassFile());
84         }
85         return document;
86     }
87
88     /**
89      * We are looking for two stack patterns, which both are related to debug session and
90      * coming from SourceLookupFacility.display(ISourceLookupResult result, IWorkbenchPage page):
91      * first is the highlighting the editor current line, corresponding to
92      * the line in the bytecode stack (light gray color),
93      * and second is the annotation the current debugger position (same line as before) in
94      * editor (light green color)
95      *
96      * This is a VERY BAD and VERY DIRTY hack, but it works.
97      * @return
98      */

99     private boolean isDebuggerCall() {
100         Exception JavaDoc e = new Exception JavaDoc();
101         StackTraceElement JavaDoc[] stackTrace = e.getStackTrace();
102         boolean stackOk = true;
103         // at 0 is our method name, and 1 id the "getDocument" call, so we start with 2
104
for (int i = 2; i < stackTrace.length; i++) {
105             StackTraceElement JavaDoc elt = stackTrace[i];
106             switch (i) {
107                 case 2 :
108                     stackOk = "getLineInformation".equals(elt.getMethodName())
109                         || "addAnnotation".equals(elt.getMethodName());
110                     break;
111                 case 3 :
112                     stackOk = "positionEditor".equals(elt.getMethodName())
113                      || "display".equals(elt.getMethodName());
114                     break;
115                 default :
116                     break;
117             }
118             if(! stackOk || i > 3){
119                 return false;
120             }
121
122             if (stackOk && i == 3) {
123                 IEditorPart activeEditor = PlatformUI.getWorkbench()
124                     .getActiveWorkbenchWindow().getActivePage()
125                     .getActiveEditor();
126                 if(activeEditor instanceof BytecodeClassFileEditor){
127                     BytecodeClassFileEditor editor = (BytecodeClassFileEditor) activeEditor;
128                     return editor.isDecompiled();
129                 }
130             }
131         }
132         return false;
133     }
134
135     public IRegion getDecompiledLineInfo(IEditorInput input, int decompiledLine) {
136         IDocument document = getDocument(input);
137         try {
138             return document.getLineInformation(decompiledLine);
139         } catch (BadLocationException e) {
140             BytecodeOutlinePlugin.log(e, IStatus.ERROR);
141         }
142         return null;
143     }
144
145     /*
146      * SourceLookupFacility.class: The code is responsible for the
147      * positioning of current debugger line during debugging Positions the text editor for
148      * the given stack frame :
149      * private void positionEditor(ITextEditor editor, IStackFrame frame)
150      * private IRegion getLineInformation(ITextEditor editor, int lineNumber)
151      *
152      * Other place for debug instruction pointer with the "wrong line" is InstructionPointerManager
153      * public void addAnnotation(ITextEditor textEditor, IStackFrame frame, Annotation annotation)
154      */

155
156     /**
157      * This class is non-functional replacement for IDocument. The only one purpose is to
158      * override getLineInformation() implementation for debug purposes
159      */

160     private static final class DocumentProxy4Debugger extends AbstractDocument {
161
162         private final IDocument delegate;
163         private final IClassFile cf;
164
165         public DocumentProxy4Debugger(IDocument delegate, IClassFile cf) {
166             super();
167             this.delegate = delegate;
168             this.cf = cf;
169         }
170
171         public IRegion getLineInformation(int line) throws BadLocationException {
172             BytecodeSourceMapper mapper = BytecodeClassFileEditor
173                 .getSourceMapper();
174
175             int decompiledLine;
176             if(line < -1){
177                 /* this is the case if debugger does not have line information in bytecode
178                  * if bytecode does not contain line info, we should at least
179                  * return the line with the first method instruction.
180                  */

181                 decompiledLine = mapper.mapDebuggerToDecompiled(cf);
182             } else {
183                 // SourceLookupFacility decrement source line by 1
184
decompiledLine = mapper.mapToDecompiled(line + 1, cf);
185                 if(decompiledLine == -1){
186                     /*
187                      * The line is from inner class (it is in another class file)
188                      * the mapping does not work for inner/anon. classes, as the debugger
189                      * expect that their source code is in our editor which is not the case for
190                      * bytecode of inner classes => for inner classes we use another strategy
191                      */

192                     return BytecodeClassFileEditor.checkForInnerClass(line, cf);
193                 }
194             }
195             // editor start lines with 1
196
return delegate.getLineInformation(decompiledLine + 1);
197         }
198     }
199
200 }
201
Popular Tags