org.netbeans.editor.SideBarFactory (Part 1)

My first investigation into org.netbeans.editor.SideBarFactory is even semi useful. It parses Java files and shows colors to distinguish between fields (green), constructors (red), and methods (blue):

Everything I know about org.netbeans.editor.SideBarFactory I learned from Andrzej Olszak who uses it in Featureous to distinguish code belonging to different features.

Here's how to create the above:

  1. Register the SideBarFactory that you're going to create (I don't know how/if this is possible via annotations, tried but failed so far):
    <folder name="Editors">
        <folder name="SideBar">
            <file name="org-demo-sidebar-DemoSidebarFactory.instance">
                <attr name="position" intvalue="3000"/>
            </file>
        </folder>
    </folder>
  2. And here's the SideBarFactory referred to above, i.e., notice that we have access to the editor component right away:
    import javax.swing.JComponent;
    import javax.swing.text.JTextComponent;
    import org.netbeans.editor.SideBarFactory;
    
    public class DemoSidebarFactory implements SideBarFactory {
    
        @Override
        public JComponent createSideBar(JTextComponent editor) {
            return new SideBarPanel(editor);
        }
        
    }

  3. And here's the SideBarPanel that you see referred to above, which I set to BorderLayout, otherwise the grey background showed through.
    public class SideBarPanel extends JPanel {
    
        SideBarPanel(JTextComponent editor) {
            super(new BorderLayout());
            add(new DrawingPanel(editor), BorderLayout.CENTER);
        }
        
    }

  4. Finally, a MigLayout panel (using "external" on the components so that control of position and size can be controlled via setBounds). All the parsing code comes from this tutorial.

    public class DrawingPanel extends JPanel {
    
        private final JTextComponent target;
        private LineCookie lc;
        private EditorCookie ec;
    
        DrawingPanel(JTextComponent jtc) {
            super(new MigLayout());
            this.target = jtc;
        }
    
        @Override
        public void paintComponent(final Graphics g) {
            super.paintComponent(g);
            final Rectangle clip = g.getClipBounds();
            if (clip.y >= 16) {
                // compensate for scrolling: 
                //marks on bottom/top edges are not drawn completely 
                //while scrolling
                clip.y -= 16;
                clip.height += 16;
            }
            g.setColor(Color.WHITE);
            g.fillRect(clip.x, clip.y, clip.width, clip.height);
            //Parse the editor:
            JTextComponent component = target;
            JavaSource js = JavaSource.forDocument(component.getDocument());
            FileObject fo = js.getFileObjects().iterator().next();
            try {
                DataObject dobj = DataObject.find(fo);
                lc = dobj.getCookie(LineCookie.class);
                ec = dobj.getCookie(EditorCookie.class);
            } catch (DataObjectNotFoundException ex) {
                Exceptions.printStackTrace(ex);
            }
            if (js != null) {
                try {
                    js.runUserActionTask(new Task<CompilationController>() {
                        @Override
                        public void run(CompilationController compilationController) 
                throws Exception {
                            compilationController.toPhase(Phase.ELEMENTS_RESOLVED);
                            Document document = compilationController.getDocument();
                            if (document != null) {
                                new MemberVisitor(compilationController).scan(
                compilationController.getCompilationUnit(), null);
                            }
                        }
                    }, true);
                } catch (IOException ex) {
                    Exceptions.printStackTrace(ex);
                }
            }
     
        }
    
        private class MemberVisitor extends TreePathScanner<Void, Void> {
            private CompilationInfo info;
            public MemberVisitor(CompilationInfo info) {
                this.info = info;
            }
            @Override
            public Void visitClass(ClassTree t, Void v) {
                Element el = info.getTrees().getElement(getCurrentPath());
                if (el != null) {
                    TypeElement te = (TypeElement) el;
                    List enclosedElements = te.getEnclosedElements();
                    SourcePositions sp = info.getTrees().getSourcePositions();
                    for (int i = 0; i < enclosedElements.size(); i++) {
                        try {
                            Element enclosedElement = (Element) enclosedElements.get(i);
                            StyledDocument doc = ec.openDocument();
                            int start = (int) sp.getStartPosition(
                    info.getCompilationUnit(), info.getTrees().getTree(enclosedElement));
                            int end = (int) sp.getEndPosition(info.getCompilationUnit(), 
                    info.getTrees().getTree(enclosedElement));
                            int startLine = NbDocument.findLineNumber(doc, start);
                            int endLine = NbDocument.findLineNumber(doc, end);
                            if (enclosedElement.getKind() == ElementKind.CONSTRUCTOR) {
                                createPanel(startLine, endLine, 1);
                            } else if (enclosedElement.getKind() == ElementKind.METHOD) {
                                createPanel(startLine, endLine, 2);
                            } else if (enclosedElement.getKind() == ElementKind.FIELD) {
                                createPanel(startLine, endLine, 3);
                            }
                        } catch (IOException ex) {
                            Exceptions.printStackTrace(ex);
                        }
                    }
                }
                return null;
            }
            private void createPanel(int startLine, int endLine, int type) {
                JPanel panel = new JPanel();
                //hack! anyone know how to map the editor to the sidebar panel?:
                panel.setBounds(0, startLine * 16+5, 25, 5);
                if (type == 1) {
                    panel.setBackground(Color.red);
                } else if (type == 2) {
                    panel.setBackground(Color.blue);
                } else if (type == 3) {
                    panel.setBackground(Color.green);
                }
                panel.setBorder(new LineBorder(Color.black, 1));
                add(panel, "external");
            }
        }
     
    }
    
     

So, you have a panel created by the SideBarFactory within the editor. And then you parse the editor and do to the panel whatever it is you need to show.

Comments:

Great tutorial Geertjan! This will be a great help for people integrating/developing their development tools for the NB IDE.

Just a comment: I think that performance could be significantly improved by avoiding the parse step in the paint method (unless there is some caching for that done behind the scenes by the parser - how about that compiler gurus?).

However, if you do this, you will need a way to stay up-to-date with changes of the editor's content and code folding. You could do this using some of the method calls:
Utilities.getDocument(target).addDocumentListener(dl);
//PostModificationDocumentListener(dl); ???
target.addFocusListener(focList);
target.addCaretListener(carList);
FoldHierarchy.get(target).addFoldHierarchyListener(fhl);

Posted by Andrzej on August 14, 2011 at 03:17 AM PDT #

Hi,
i think it's quite good idea and may be useful but i always wanted this functionality to lines where awt.Color is declared.
Visualy in my opinion lines are too long... :)

Posted by guest on August 16, 2011 at 04:53 AM PDT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Geertjan Wielenga (@geertjanw) is a Principal Product Manager in the Oracle Developer Tools group living & working in Amsterdam. He is a Java technology enthusiast, evangelist, trainer, speaker, and writer. He blogs here daily.

The focus of this blog is mostly on NetBeans (a development tool primarily for Java programmers), with an occasional reference to NetBeans, and sometimes diverging to topics relating to NetBeans. And then there are days when NetBeans is mentioned, just for a change.

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
12
13
14
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today