The Source for Java Technology Collaboration
User: Password:



Rich Unger's Blog

May 2006 Archives


A simple class browser

Posted by richunger on May 25, 2006 at 11:26 PM | Permalink | Comments (2)

At JavaOne, Geertjan gave a talk on building an IDE for a specific framework (Wicket). I work on a similar project for an in-house framework. One feature that I thought would be useful is a convenient Java Class selector.

I really like the "Fast Open" selector that you get from the "Navigate...Go To Class" menu item. I looked at the code, and was dismayed to see that it was hard-coded for opening classes, and was not reusable for just selecting a class for another purpose (such as inserting a class reference in an xml file).

So, I sat down to write such a widget, and was pleased to be able to put something pretty featureful together in a couple of hours. I use the MDR API for querying classes.

classbrowser.jpg

The list box contains all classes whose classname (without package) starts with the substring in the text field. The list selection is the value returned by the dialog.

Here's a little tour of the code:

public class ClassSelector extends javax.swing.JDialog
{
    // the data for the ListModel
    private List candidates = new ArrayList();

    // an example usage
    public static String getFullyQualifiedClassName()
    {
        ClassSelector cs = new ClassSelector();
        cs.setVisible(true);
        JCClass c = cs.getSelection();
        return c == null ? null : c.getFullName();
    }

    public ClassSelector()
    {
        this(WindowManager.getDefault().getMainWindow(), true);
    }

    public ClassSelector(java.awt.Frame parent, boolean modal)
    {
        super(parent, modal);
        initComponents();
    }

    public JCClass getSelection()
    {
        return (JCClass)matchingList.getSelectedValue();
    }

    private void initComponents()
    {
        // matisse-generated code here
    }

    private void matchingListKeyPressed(java.awt.event.KeyEvent evt)
    {
        // make ENTER work the same as the OK button
        if (evt.getKeyCode() == KeyEvent.VK_ENTER)
            okButtonActionPerformed(null);
    }

    private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt)
    {
        matchingList.setSelectedIndex(-1);
        dispose();
    }

    private void okButtonActionPerformed(java.awt.event.ActionEvent evt)
    {
        dispose();
    }

    private ListModel createListModel()
    {
        return new Model();
    }

    private ListCellRenderer createCellRenderer()
    {
        return new Renderer();
    }

    private class Model extends KeyAdapter implements ListModel
    {
        private List listeners = new ArrayList();

        public Model()
        {
            classNameField.addKeyListener(this);
        }

        public int getSize()
        {
            return candidates.size();
        }

        public Object getElementAt(int index)
        {
            return candidates.get(index);
        }

        public void addListDataListener(ListDataListener l)
        {
            listeners.add(l);
        }

        public void removeListDataListener(ListDataListener l)
        {
            listeners.remove(l);
        }

        private void fireListChange()
        {
            ListDataEvent evt = new ListDataEvent(this,
                    ListDataEvent.CONTENTS_CHANGED, 0, getSize()-1);
            for (ListDataListener l : listeners)
            {
                l.contentsChanged(evt);
            }
        }

        private void refresh()
        {
            String str = classNameField.getText();

            if (str == null || str.trim().length() == 0)
            {
                // text field is blank, so don't do a query
                if (!candidates.isEmpty())
                {
                    candidates.clear();
                    fireListChange();
                }
            }
            else
            {
                // clear out the old query results, and do a new query
                str = str.trim();
                candidates.clear();

                JCFinder finder = JCFinderFactory.getDefault().getGlobalFinder();

                // TODO: make case insensitive
                List classes = finder.findClasses(
                        null, // don't specify a package
                        str, 
                        false); // don't require an exact match

                candidates.addAll(classes);

                fireListChange();

                if (!candidates.isEmpty() && matchingList.getSelectedIndex() == -1)
                {
                    // if there's no selection at this point, select the first item
                    matchingList.setSelectedIndex(0);
                }

            }
        }

        /** each time a letter is typed, do a new query */
        public void keyReleased(KeyEvent e)
        {
            refresh();
        }
    }

    private class Renderer implements ListCellRenderer
    {
        public Component getListCellRendererComponent(
                JList list, Object value, int index,
                boolean isSelected, boolean cellHasFocus)
        {
            JCClass c = (JCClass)value;
            String str = c.getName() + " (" + c.getPackageName() + ")";
            JLabel label = new JLabel(str);
            if (isSelected)
            {
                label.setBackground(Color.BLUE);
                label.setForeground(Color.WHITE);
                label.setOpaque(true);
            }
            return label;
        }
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton cancelButton;
    private javax.swing.JTextField classNameField;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JList matchingList;
    private javax.swing.JButton okButton;
    // End of variables declaration//GEN-END:variables

}

Neat, eh? The full code is here.

photographic proof!

Posted by richunger on May 23, 2006 at 12:12 PM | Permalink | Comments (2)

I have been sent (anonymously) photographic evidence of the netbeans/eclipse summit ... err ... beer run. netbeans-meets-eclipse-1.jpg netbeans-meets-eclipse-2.jpg

The netbeans and eclipse developers get along just fine

Posted by richunger on May 22, 2006 at 12:39 PM | Permalink | Comments (4)

I saw this javalobby discussion on roumen's antics today, and I thought I'd put my 2 cents in.

Yes, the products compete. Yes, there's a lot of heated debate and personal attacks on blogs and message boards.

You generally don't see that from the folks who actually *work* on these products.

Last year, I wrote an entry on a meeting of the minds between architects from the eclipse and netbeans platforms. The other day, I was out at the Thirsty Bear with about a dozen folks from eclipse, netbeans, and OSGi. We discussed modular architecture, opinions on "superpackages" in dolphin, software update methodologies, and how utterly rediculous the flame wars are getting.

We were comparing notes. There was no ideas being held back. The OSGi folks wanted to know what it would take to get NB to talk their protocol (a thorny topic I'll leave to the real experts). I asked about how eclipse handles some project and workspace layout stuff under the hood. It was an interesting discussion, and we all had fun. Competitors, to be sure, but in a very open, healthy way.

So, what about Roumen's prank? Well, I can't say I would have done the same thing. Eclipse paid for their booth, and I think it was a bit inappropriate. But, no real damage done, and I think Steve Northover's comments in Javalobby reflect that there's no real ill will among the folks in the trenches. Roumen was just trying to have a little fun, and he misread the atmosphere at the pavilion. Big deal. It's over. I don't think anyone lost any customers over it. I think it's very likely that the brouhaha was more about Eclipse being disappointed with the location of their booth than about anything Roumen did.

As an aside, I was in the "Eclipse Callisto" talk, and someone asked, "Are you happy with the amount of coverage Eclipse is getting at this conference?" The questioner was implying that the stepped-up NB coverage was Sun shilling, and Eclipse was getting short shrift. The answer from the Eclipse speakers, however, was "we are very pleased." In fact, they said they were late submitting that very talk, and Sun made a special exception to admit them.

People look for controversy, but the real story here is just how friendly this competition is.





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds