Package de.loskutov.bco.editors

Source Code of de.loskutov.bco.editors.BytecodeClassFileEditor$InputUpdater

/* $Id: BytecodeClassFileEditor.java,v 1.9 2008-07-02 19:18:35 andrei Exp $ */

package de.loskutov.bco.editors;

import java.lang.reflect.Constructor;
import java.util.BitSet;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.ui.actions.IToggleBreakpointsTarget;
import org.eclipse.jdt.core.IBuffer;
import org.eclipse.jdt.core.IBufferChangedListener;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.IInitializer;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaModelStatusConstants;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.ISourceReference;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.debug.core.IJavaReferenceType;
import org.eclipse.jdt.internal.ui.JavaUIStatus;
import org.eclipse.jdt.internal.ui.javaeditor.ClassFileDocumentProvider;
import org.eclipse.jdt.internal.ui.javaeditor.ClassFileEditor;
import org.eclipse.jdt.internal.ui.javaeditor.ExternalClassFileEditorInput;
import org.eclipse.jdt.internal.ui.javaeditor.IClassFileEditorInput;
import org.eclipse.jdt.internal.ui.javaeditor.InternalClassFileEditorInput;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITextViewerExtension5;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.IDocumentProvider;

import de.loskutov.bco.BytecodeOutlinePlugin;
import de.loskutov.bco.asm.DecompiledClass;
import de.loskutov.bco.preferences.BCOConstants;
import de.loskutov.bco.ui.JdtUtils;

/**
* A "better" way to hook into JDT...
* @author Eugene Kuleshov, V. Grishchenko, Jochen Klein, Andrei Loskutov
*/
public class BytecodeClassFileEditor extends ClassFileEditor
    implements
        ClassFileDocumentProvider.InputChangeListener {

    private final InputUpdater fInputUpdater;
    public static final String ID = "de.loskutov.bco.editors.BytecodeClassFileEditor";
    public static final String MARK = "// class version ";
    /** the modes (flags) for the decompiler */
    private final BitSet decompilerFlags;
    /** is not null only on class files with decompiled source */
    private static BytecodeSourceMapper sourceMapper;
    private BytecodeDocumentProvider fClassFileDocumentProvider;
    private boolean hasMappedSource;
    private boolean decompiled;
    private boolean initDone;
    private boolean sourceAttachmentPossible;

    /**
     * Constructor for JadclipseClassFileEditor.
     */
    public BytecodeClassFileEditor() {
        super();
        if (sourceMapper == null) {
            sourceMapper = new BytecodeSourceMapper();
        }
        fInputUpdater = new InputUpdater();
        setDocumentProvider(getClassFileDocumentProvider());
        setEditorContextMenuId("#ClassFileEditorContext"); //$NON-NLS-1$
        setRulerContextMenuId("#ClassFileRulerContext"); //$NON-NLS-1$
        setOutlinerContextMenuId("#ClassFileOutlinerContext"); //$NON-NLS-1$
        // don't set help contextId, we install our own help context

        decompilerFlags = new BitSet();
        // TODO take from preferences and/or last editor memento
        decompilerFlags.set(BCOConstants.F_SHOW_LINE_INFO, true);
        decompilerFlags.set(BCOConstants.F_SHOW_VARIABLES, true);
        decompilerFlags.set(BCOConstants.F_SHOW_RAW_BYTECODE, false);
        setSourceAttachmentPossible(true);
    }

    /**
     * @return the hasMappedSource
     */
    protected boolean hasMappedSource() {
        return hasMappedSource;
    }

    /**
     * @param hasMappedSource the hasMappedSource to set
     */
    protected void setHasMappedSource(boolean hasMappedSource) {
        this.hasMappedSource = hasMappedSource;
    }

    private ClassFileDocumentProvider getClassFileDocumentProvider() {
        if (fClassFileDocumentProvider == null) {
            fClassFileDocumentProvider = new BytecodeDocumentProvider(this);
        }
        return fClassFileDocumentProvider;
    }

    public void setDecompilerFlag(int flag, boolean value) {
        decompilerFlags.set(flag, value);
    }

    public boolean getDecompilerFlag(int flag) {
        return decompilerFlags.get(flag);
    }

    /*
     * @see IEditorPart#init(IEditorSite, IEditorInput)
     */
    public void init(IEditorSite site, IEditorInput input)
        throws PartInitException {
        input = doOpenBuffer(input, false, true);
        super.init(site, input);
    }

    /**
     * Sets editor input only if buffer was actually opened.
     * @param force if <code>true</code> initialize no matter what
     * @param reuseSource true to show source code if available
     */
    public void doSetInput(boolean force, boolean reuseSource) {
        IEditorInput input = getEditorInput();
        input = doOpenBuffer(input, force, reuseSource);
        if (input != null) {
            try {
                doSetInput(input);
            } catch (Exception e) {
                BytecodeOutlinePlugin.log(e, IStatus.ERROR);
            }
        }
    }

    /*
     * @see AbstractTextEditor#doSetInput(IEditorInput)
     * ClassFileDocumentProvider.setDocumentContent(IDocument, IEditorInput, String) line:
     * 202 ClassFileDocumentProvider(StorageDocumentProvider).createDocument(Object) line:
     * 228 ClassFileDocumentProvider.createDocument(Object) line: 247
     * ClassFileDocumentProvider.createElementInfo(Object) line: 275
     * ClassFileDocumentProvider(AbstractDocumentProvider).connect(Object) line: 398
     * BytecodeClassFileEditor(AbstractTextEditor).doSetInput(IEditorInput) line: 3063
     * BytecodeClassFileEditor(StatusTextEditor).doSetInput(IEditorInput) line: 173
     * BytecodeClassFileEditor(AbstractDecoratedTextEditor).doSetInput(IEditorInput) line:
     * 1511 BytecodeClassFileEditor(JavaEditor).internalDoSetInput(IEditorInput) line:
     * 2370 BytecodeClassFileEditor(JavaEditor).doSetInput(IEditorInput) line: 2343
     * BytecodeClassFileEditor.doSetInput(IEditorInput) line: 201
     * AbstractTextEditor$17.run(IProgressMonitor) line: 2396
     */
    protected void doSetInput(IEditorInput input) throws CoreException {

        input = transformEditorInput(input);
        if (!(input instanceof IClassFileEditorInput)) {
            throw new CoreException(JavaUIStatus.createError(
                IJavaModelStatusConstants.INVALID_RESOURCE_TYPE,
                "invalid input", // JavaEditorMessages.ClassFileEditor_error_invalid_input_message,
                null));
        }

        IDocumentProvider documentProvider = getDocumentProvider();
        if (documentProvider instanceof ClassFileDocumentProvider) {
            ((ClassFileDocumentProvider) documentProvider)
                .removeInputChangeListener(this);
        }

        super.doSetInput(input);

        documentProvider = getDocumentProvider();
        if (documentProvider instanceof ClassFileDocumentProvider) {
            ((ClassFileDocumentProvider) documentProvider)
                .addInputChangeListener(this);
        }
    }

    /**
     * @return <code>true</code> if this editor displays decompiled source,
     * <code>false</code> otherwise
     */
    public boolean isDecompiled() {
        return decompiled;
    }

    private IEditorInput doOpenBuffer(IJavaReferenceType type,
        IClassFile parent, boolean externalClass) {
        IClassFile classFile = null;
        try {
            classFile = JdtUtils.getInnerType(parent, getSourceMapper()
                .getDecompiledClass(parent), type.getSignature());
        } catch (DebugException e) {
            BytecodeOutlinePlugin.log(e, IStatus.ERROR);
        }
        return doOpenBuffer(classFile, externalClass);
    }

    private IEditorInput doOpenBuffer(IClassFile classFile, boolean externalClass) {
        IEditorInput input = null;
        if (classFile == null) {
            return null;
        }
        if (externalClass) {
            // TODO create external input, but we need a file object here...
        } else {
            input = transformEditorInput(classFile);
        }
        return doOpenBuffer(input, false, true);
    }

    private IEditorInput doOpenBuffer(IEditorInput input, boolean force,
        boolean reuseSource) {
        if (input instanceof IClassFileEditorInput) {
            IClassFile cf = ((IClassFileEditorInput) input).getClassFile();
            String origSrc = getAttachedJavaSource(cf, force);
            if (origSrc != null && !hasMappedSource) {
                // remember, that the JDT knows where the real source is and can show it
                setHasMappedSource(true);
            }

            if (origSrc == null || (force && !reuseSource)) {
                setDecompiled(true);
                char[] src;
                if (input instanceof ExternalClassFileEditorInput) {
                    ExternalClassFileEditorInput extInput = (ExternalClassFileEditorInput) input;
                    src = getSourceMapper().getSource(
                        extInput.getFile(), cf, decompilerFlags);
                } else {
                    src = getSourceMapper().getSource(cf, decompilerFlags);
                }
                changeBufferContent(cf, src);
            } else {
                setDecompiled(false);
            }
        } else if (input instanceof FileEditorInput) {
            FileEditorInput fileEditorInput = (FileEditorInput) input;
            // make class file from that
            IClassFileEditorInput cfi = (IClassFileEditorInput) transformEditorInput(input);
            // return changed reference
            input = cfi;
            setDecompiled(true);
            IClassFile cf = cfi.getClassFile();
            char[] src = getSourceMapper().getSource(
                fileEditorInput.getFile(), cf, decompilerFlags);
            changeBufferContent(cf, src);
        }
        return input;
    }

    private void setDecompiled(boolean decompiled) {
        boolean oldDecompiled = this.decompiled;
        this.decompiled = decompiled;
        if(initDone && oldDecompiled != decompiled) {
            if(decompiled) {
                // prevent multiple errors in ASTProvider which fails to work without source
                uninstallOccurrencesFinder();
            } else {
                // install again if source is available
                installOccurrencesFinder(true);
            }
        }
    }

    private String getAttachedJavaSource(IClassFile cf, boolean force) {
        String origSrc = null;
        if (force) {
            IBuffer buffer = BytecodeBufferManager.getBuffer(cf);
            if (buffer != null) {
                BytecodeBufferManager.removeBuffer(buffer);
            }
        }
        try {
            origSrc = cf.getSource();
            if (origSrc != null && origSrc.startsWith(MARK)) {
                // this is NOT orig. sourse, but cached content
                origSrc = null;
            }
        } catch (JavaModelException e) {
            BytecodeOutlinePlugin.log(e, IStatus.ERROR);
        }
        return origSrc;
    }

    private void changeBufferContent(IClassFile cf, char[] src) {
        IBuffer buffer = BytecodeBufferManager.getBuffer(cf);

        // i'm not sure if we need to create buffer each time -
        // couldn't we reuse existing one (if any)?
        // - seems that without "create" some listener didn't get notifications about
        // changed content
        boolean addBuffer = false;

        // if(buffer == null) {
        buffer = BytecodeBufferManager.createBuffer(cf);
        addBuffer = true;
        // }
        if (src == null) {
            src = new char[]{'\n', '/', '/', 'E', 'r', 'r', 'o', 'r'};
        }
        buffer.setContents(src);
        if (addBuffer) {
            BytecodeBufferManager.addBuffer(buffer);
            buffer.addBufferChangedListener((IBufferChangedListener) cf);
        }
    }

    /*
     * @see JavaEditor#getElementAt(int)
     */
    protected IJavaElement getElementAt(int offset) {

        IClassFile classFile = getClassFile();
        if (classFile == null) {
            return null;
        }
        IJavaElement result = null;
        if (isDecompiled()) {
            IDocument document = getDocumentProvider().getDocument(
                getEditorInput());
            try {
                // XXX have test if the requested line is from bytecode or sourcecode?!?
                if(document.getLength() > offset){
                    int lineAtOffset = document.getLineOfOffset(offset);
                    // get DecompiledMethod from line, then get JavaElement with same
                    // signature, because we do not have offsets or lines in the class file,
                    // only java elements...
                    result = getSourceMapper().findElement(classFile, lineAtOffset);
                }
            } catch (BadLocationException e) {
                BytecodeOutlinePlugin.log(e, IStatus.ERROR);
            }
        } else {
            try {
                result = classFile.getElementAt(offset);
            } catch (JavaModelException e) {
                BytecodeOutlinePlugin.log(e, IStatus.ERROR);
            }
        }

        return result;
    }

    public IClassFile getClassFile() {
        IEditorInput editorInput = getEditorInput();
        if (!(editorInput instanceof IClassFileEditorInput)) {
            return null;
        }
        return ((IClassFileEditorInput) editorInput).getClassFile();
    }

    protected void setSelection(ISourceReference reference, boolean moveCursor) {
        if (reference == null) {
            if (moveCursor) {
                resetHighlightRange();
            }
            return;
        }
        try {
            ISourceRange range = null;
            int offset;
            int length;
            if (isDecompiled() && isSupportedMember(reference)) {

                // document lines count starts with 1 and not with 0
                int decompLine = -1
                    + getSourceMapper().getDecompiledLine(
                        (IMember) reference, getClassFile());
                if(decompLine < 0){
                    return;
                }
                IRegion region = ((BytecodeDocumentProvider) getDocumentProvider())
                    .getDecompiledLineInfo(getEditorInput(), decompLine);
                if (region == null) {
                    return;
                }
                offset = region.getOffset();
                length = region.getLength();
            } else if (!isDecompiled()) {
                range = reference.getSourceRange();
                if (range == null) {
                    return;
                }
                offset = range.getOffset();
                length = range.getLength();
            } else {
                return;
            }

            if (offset > -1 && length > 0) {
                setHighlightRange(offset, length, moveCursor);
            }

            if ((reference instanceof IMember) && !isDecompiled()) {
                IMember member = (IMember) reference;
                range = member.getNameRange();
                if (range != null) {
                    offset = range.getOffset();
                    length = range.getLength();
                }
            }
            if (moveCursor && offset > -1 && length > 0) {
                ISourceViewer sourceViewer = getSourceViewer();
                if (sourceViewer != null) {
                    sourceViewer.revealRange(offset, length);
                    sourceViewer.setSelectedRange(offset, length);
                }
            }
            return;
        } catch (Exception e) {
            BytecodeOutlinePlugin.log(e, IStatus.ERROR);
        }

        if (moveCursor) {
            resetHighlightRange();
        }
    }

    private boolean isSupportedMember(ISourceReference reference) {
        // TODO this condition is not enough. We could have inner/anon. classes
        // as selection source (they are displayed in the outline),
        // but they are not in the decompiled class,
        // so we need to filter "external" elements too, even if they are methods...

        // we could also later point to the IField's, but currently it is not supported
        return reference instanceof IMethod
            || reference instanceof IInitializer;
    }

    public Object getAdapter(Class required) {
        if (IToggleBreakpointsTarget.class == required) {

            // TODO implement own adapter for toggle breakpoints, because the default one
            // could not find java elements in our document and therefore could not
            // create a Java breakpoint.
            // see org.eclipse.jdt.internal.debug.ui.actions.ToggleBreakpointAdapter
            // IMember member =
            // ActionDelegateHelper.getDefault().getCurrentMember(selection);
            // and the ActionDelegateHelper looks with the given offset at classfile or
            // compilation unit, but our offset is completely different to Java source
            // code
            super.getAdapter(required);
        }

        return super.getAdapter(required);
    }

    protected ISourceReference computeHighlightRangeSourceReference() {
        if (!isDecompiled()) {
            return super.computeHighlightRangeSourceReference();
        }

        ISourceViewer sourceViewer = getSourceViewer();
        if (sourceViewer == null) {
            return null;
        }
        StyledText styledText = sourceViewer.getTextWidget();
        if (styledText == null) {
            return null;
        }

        int caret = 0;
        if (sourceViewer instanceof ITextViewerExtension5) {
            ITextViewerExtension5 extension = (ITextViewerExtension5) sourceViewer;
            caret = extension.widgetOffset2ModelOffset(styledText
                .getCaretOffset());
        } else {
            int offset = sourceViewer.getVisibleRegion().getOffset();
            caret = offset + styledText.getCaretOffset();
        }

        IJavaElement element = getElementAt(caret);

        if (!(element instanceof ISourceReference)) {
            return null;
        }
        return (ISourceReference) element;
    }

    public void showHighlightRangeOnly(boolean showHighlightRangeOnly) {
        // disabled as we currently do not support "partial" view on selected
        // elements
        // super.showHighlightRangeOnly(showHighlightRangeOnly);
    }

    protected void updateOccurrenceAnnotations(ITextSelection selection,
        CompilationUnit astRoot) {
        // disabled for bytecode as we currently do not support "occurencies" highlighting
        if(hasMappedSource()) {
            super.updateOccurrenceAnnotations(selection, astRoot);
        }
    }

    /**
     * Updater that takes care of minimizing changes of the editor input.
     */
    private class InputUpdater implements Runnable {

        /** Has the runnable already been posted? */
        private boolean fPosted = false;
        /** Editor input */
        private IClassFileEditorInput fClassFileEditorInput;

        public InputUpdater() {
            //
        }

        /*
         * @see Runnable#run()
         */
        public void run() {

            IClassFileEditorInput input;
            synchronized (this) {
                input = fClassFileEditorInput;
            }

            try {
                if (getSourceViewer() != null) {
                    setInput(input);
                }
            } finally {
                synchronized (this) {
                    fPosted = false;
                }
            }
        }

        /**
         * Posts this runnable into the event queue if not already there.
         * @param input the input to be set when executed
         */
        public void post(IClassFileEditorInput input) {

            synchronized (this) {
                if (fPosted) {
                    if (input != null && input.equals(fClassFileEditorInput)) {
                        fClassFileEditorInput = input;
                    }
                    return;
                }
            }

            if (input != null && input.equals(getEditorInput())) {
                ISourceViewer viewer = getSourceViewer();
                if (viewer != null) {
                    StyledText textWidget = viewer.getTextWidget();
                    if (textWidget != null && !textWidget.isDisposed()) {
                        synchronized (this) {
                            fPosted = true;
                            fClassFileEditorInput = input;
                        }
                        textWidget.getDisplay().asyncExec(this);
                    }
                }
            }
        }
    }

    /*
     * @see JavaEditor#getCorrespondingElement(IJavaElement)
     */
    protected IJavaElement getCorrespondingElement(IJavaElement element) {
        IClassFile classFile = getClassFile();
        if (classFile == null) {
            return super.getCorrespondingElement(element);
        }
        if (classFile.equals(element.getAncestor(IJavaElement.CLASS_FILE))) {
            return element;
        }
        return super.getCorrespondingElement(element);
    }

    /*
     * 1GEPKT5: ITPJUI:Linux - Source in editor for external classes is editable Removed
     * methods isSaveOnClosedNeeded and isDirty. Added method isEditable.
     */
    /*
     * @see org.eclipse.ui.texteditor.AbstractTextEditor#isEditable()
     */
    public boolean isEditable() {
        return isDecompiled();
    }

    /*
     * @see org.eclipse.ui.texteditor.AbstractTextEditor#isEditorInputReadOnly()
     * @since 3.2
     */
    public boolean isEditorInputReadOnly() {
        return !isDecompiled();
    }

    /*
     * @see ITextEditorExtension2#isEditorInputModifiable()
     * @since 2.1
     */
    public boolean isEditorInputModifiable() {
        return isDecompiled();
    }

    public boolean isSaveAsAllowed() {
        return isDecompiled();
    }

    /**
     * Translates the given object into an <code>IClassFileEditorInput</code>
     * @param input the object to be transformed if necessary
     * @return the transformed editor input
     */
    protected IClassFileEditorInput transformEditorInput(Object input) {

        if (input instanceof IFileEditorInput) {
            IFile file = ((IFileEditorInput) input).getFile();
            Constructor cons;
            try {
                cons = ExternalClassFileEditorInput.class
                    .getDeclaredConstructor(new Class[]{IFile.class});
                cons.setAccessible(true);
                IClassFileEditorInput classFileInput = (IClassFileEditorInput) cons
                    .newInstance(new Object[]{file});
                return classFileInput;
            } catch (Exception e) {
                BytecodeOutlinePlugin.log(e, IStatus.ERROR);
            }
        } else if (input instanceof IClassFileEditorInput) {
            return (IClassFileEditorInput) input;
        } else if (input instanceof IClassFile) {
            return new InternalClassFileEditorInput((IClassFile) input);
        }

        return null;
    }

    /*
     * @see IWorkbenchPart#createPartControl(Composite)
     */
    public void createPartControl(Composite parent) {
        super.createPartControl(parent);
        initDone = true;
    }

    /*
     * @see ClassFileDocumentProvider.InputChangeListener#inputChanged(IClassFileEditorInput)
     */
    public void inputChanged(final IClassFileEditorInput input) {
        Runnable updateInput = new Runnable() {
            public void run() {
                fInputUpdater.post(input);
                IClassFile cf = input.getClassFile();
                try {
                    String source = cf.getSource();
                    setDecompiled(source != null && source.startsWith(MARK));
                } catch (JavaModelException e) {
                    BytecodeOutlinePlugin.log(e, IStatus.ERROR);
                }

            }
        };
        if(Display.getCurrent() == null){
            Display.getDefault().asyncExec(updateInput);
        } else {
            updateInput.run();
        }
    }

    /*
     * @see org.eclipse.ui.IWorkbenchPart#dispose()
     */
    public void dispose() {
        // http://bugs.eclipse.org/bugs/show_bug.cgi?id=18510
        IDocumentProvider documentProvider = getDocumentProvider();
        if (documentProvider instanceof ClassFileDocumentProvider) {
            ((ClassFileDocumentProvider) documentProvider)
                .removeInputChangeListener(this);
        }

        IClassFile classFile = getClassFile();
        BytecodeBufferManager.removeBuffer(BytecodeBufferManager
            .getBuffer(classFile));
        super.dispose();
    }

    public static BytecodeSourceMapper getSourceMapper() {
        return sourceMapper;
    }

    /**
     * Check if we can show an inner class, which was declared in the source code of the
     * given parent class and which could have the bytecode for the given source line.
     *
     * If both are true, then this method changes the input of the bytecode editor
     * with the given class file (if any) to the inner class, or opens a new editor with
     * the inner class.
     *
     * @param sourceLine requested source line (from debugger)
     * @param parent expected parent class file
     * @return The region in the inner class (if inner class could be found), or an empty
     * zero-based region.
     */
    public static IRegion checkForInnerClass(int sourceLine, IClassFile parent) {
        IRegion region = new Region(0, 0);

        // get the editor with given class file, if any
        BytecodeClassFileEditor editor = getBytecodeEditor(parent);
        if (editor == null) {
            return region;
        }

        // get the inner class type according to the debugger stack frame, if any
        IJavaReferenceType debugType = sourceMapper.getLastTypeInDebugger();
        if (debugType == null) {
            return region;
        }

        boolean externalClass = editor.getEditorInput() instanceof ExternalClassFileEditorInput;
        IEditorInput input = null;
        // check if it is a inner class from the class in editor
        if (!hasInnerClass(debugType, parent)) {
            // not only inner classes could be defined in the same source file, but also
            // local types (non public non inner classes in the same source file)
            IClassFile classFile = getLocalTypeClass(debugType, parent);
            if(classFile != null){
                input = editor.doOpenBuffer(classFile, externalClass);
            }
        } else {
            // if both exists, replace the input to the inner class
            input = editor.doOpenBuffer(debugType, parent, externalClass);
        }

        if (input == null) {
            return region;
        }

        /*
         * Now we change editor input from parent class to child class.
         * It will change editor title too, so the user will see a new class.
         * After this change, we could finally compute right source line for current stack
         */
        try {
            editor.doSetInput(input);
        } catch (CoreException e) {
            BytecodeOutlinePlugin.log(e, IStatus.ERROR);
            return region;
        }

        // and then map given source line to the decompiled code
        int decompiledLine = sourceMapper.mapToDecompiled(
            sourceLine + 1, editor.getClassFile());

        if (decompiledLine >= 0) {
            // and get the requested line information
            try {
                region = editor.getDocumentProvider().getDocument(input)
                    .getLineInformation(decompiledLine);
            } catch (BadLocationException e) {
                BytecodeOutlinePlugin.log(e, IStatus.ERROR);
            }
        }
        return region;
    }

    /**
     * @param debugType
     * @param parent
     * @return non public class (local type class) with the name from "debugType" and
     * which source was in the "parent" class. Null if no class file could be found.
     */
    private static IClassFile getLocalTypeClass(IJavaReferenceType debugType, IClassFile parent) {
        try {
            IType type = parent.getType();
            if((type.isLocal()) || (type.isMember())) {
                // local type could not be defined in local or inner classes
                return null;
            }
            // debugType.getSignature() == Lpackage/name/with/slashes/className;
            String binarySignature = debugType.getSignature();
            // get only type name from binary signature
            int idx = binarySignature.lastIndexOf('/');
            if(idx > 0 && idx < binarySignature.length() - 1){
                String name = binarySignature.substring(idx + 1);
                if(name.charAt(name.length() - 1) == ';'){
                    name = name.substring(0, name.length() - 1);
                }
                return type.getPackageFragment().getClassFile(name + ".class");
            }
        } catch (Exception e) {
            BytecodeOutlinePlugin.log(e, IStatus.ERROR);
        }
        return null;
    }

    private static boolean hasInnerClass(IJavaReferenceType debugType,
        IClassFile parent) {
        try {
            String parentName = parent.getType().getFullyQualifiedName();
            String childName = debugType.getName();
            return childName != null && childName.startsWith(parentName + "$");
        } catch (Exception e) {
            BytecodeOutlinePlugin.log(e, IStatus.ERROR);
        }
        return false;
    }

    /**
     * Returns the package fragment root of this file.
     *
     * @param file the class file
     * @return the package fragment root of the given class file
     */
    IPackageFragmentRoot getPackageFragmentRoot(IClassFile file) {

        IJavaElement element= file.getParent();
        while (element != null && element.getElementType() != IJavaElement.PACKAGE_FRAGMENT_ROOT) {
            element= element.getParent();
        }

        return (IPackageFragmentRoot) element;
    }

    private static BytecodeClassFileEditor getBytecodeEditor(IClassFile parent) {
        IEditorReference[] editorReferences = PlatformUI.getWorkbench()
            .getActiveWorkbenchWindow().getActivePage().getEditorReferences();
        for (int i = 0; i < editorReferences.length; i++) {
            IEditorPart editor = editorReferences[i].getEditor(false);
            if (editor instanceof BytecodeClassFileEditor) {
                BytecodeClassFileEditor bytecodeEditor = (BytecodeClassFileEditor) editor;
                if (parent.equals((bytecodeEditor).getClassFile())) {
                    return bytecodeEditor;
                }
            }
        }
        return null;
    }

    public ITextSelection convertSelection(ITextSelection textSelection,
        boolean toDecompiled) {
        int startLine = textSelection.getStartLine();
        int newLine;
        if (toDecompiled) {
            newLine = sourceMapper.mapToDecompiled(
                startLine + 1, getClassFile()) + 1;
        } else {
            newLine = sourceMapper.mapToSource(startLine, getClassFile()) - 1;
        }
        if(newLine < 0) {
            return null;
        }
        IDocument document = getDocumentProvider()
            .getDocument(getEditorInput());
        try {
            int lineOffset = document.getLineOffset(newLine);
            return new TextSelection(lineOffset, 0);
        } catch (BadLocationException e) {
            BytecodeOutlinePlugin.log(e, IStatus.ERROR);
        }
        return null;
    }

    public int getSourceLine(ITextSelection bytecodeSelection) {
        int startLine = bytecodeSelection.getStartLine();
        return sourceMapper.mapToSource(startLine, getClassFile()) - 1;
    }

    public ITextSelection convertLine(int sourceLine) {
        int newLine = sourceMapper.mapToDecompiled(
            sourceLine + 1, getClassFile()) + 1;
        IDocument document = getDocumentProvider()
            .getDocument(getEditorInput());
        try {
            int lineOffset = document.getLineOffset(newLine);
            return new TextSelection(lineOffset, 0);
        } catch (BadLocationException e) {
            BytecodeOutlinePlugin.log(e, IStatus.ERROR);
        }
        return null;
    }

    public int getBytecodeInstructionAtLine(int line) {
        if (isDecompiled()) {
            DecompiledClass decompiledClass = sourceMapper.getDecompiledClass(getClassFile());
            if(line > 0 && decompiledClass != null) {
                return decompiledClass.getBytecodeInsn(line);
            }
        }
        return -1;
    }

    public void setSourceAttachmentPossible(boolean sourceAttachmentPossible) {
        this.sourceAttachmentPossible = sourceAttachmentPossible;
    }

    public boolean isSourceAttachmentPossible() {
        return sourceAttachmentPossible && isDecompiled() && !hasMappedSource();
    }
}
TOP

Related Classes of de.loskutov.bco.editors.BytecodeClassFileEditor$InputUpdater

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.