Accessing the Windows registry through the preferences API

On Windows, the preferences API makes use of a DLL to access program-specific properties in the Windows Registry. The following example uses reflection to access private native methods in the preferences API and allows you to get REG_SZ values from the Windows Registry.

For example, to get the IE start page on my machine, I executed:

C:registrytest> c:jdk1.4binjava Main "HKEY_CURRENT_USER\Softw --
are\Microsoft\Internet Explorer\Main" "start page"

http://www.google.com/

It’s a demonstration and works only with JDK1.4. Use at your own risk.

Main.java:

import java.lang.reflect.*;
import java.util.prefs.*;
import java.util.*;
import java.io.*;
 
public class Main
{
   /* Windows security masks */
   private final static int KEY_QUERY_VALUE = 1;
 
   /* Constants used to interpret returns of native functions    */
   private final static int NATIVE_HANDLE = 0;
   private final static int ERROR_CODE = 1;
 
   /*  Windows error codes. */
   private final static int ERROR_SUCCESS = 0;
 
   public static void main(String[] args) {
      if (args.length != 2) {
         System.out.println("Usage: java Main path key");
         System.out.print("  eg.: java Main "HKEY_CURRENT_USER\");
         System.out.print("\Software\\Microsoft\\Internet Explorer\\Main"");
         System.out.println(" "start page"");
         System.exit(1);
      }
 
      try {
         int hkey = getHKEY(args[0]);
         byte[] WINDOWS_ROOT_PATH = stripHKEY(args[0]);
         String key = args[1];
         System.out.println(getValue(hkey, WINDOWS_ROOT_PATH, key));
      }
      catch(Exception e) {
         System.out.println(e.getMessage());
      }
   }
 
   public static int getHKEY(String path) throws Exception {
      if (path.startsWith("HKEY_CURRENT_USER")) {
         return 0x80000001;
      }
      else if (path.startsWith("HKEY_LOCAL_MACHINE")) {
         return 0x80000002;
      }
      else {
         throw new Exception("Path should start with HKEY_CURRENT_USER " + 
                                                 "or HKEY_LOCAL_MACHINE");
      }
   }
 
   public static byte[] stripHKEY(String path) {
      int beginIndex = path.indexOf("\\");
      return stringToByteArray(path.substring(beginIndex+2));
   }
 
   public static String getValue(int hkey, byte[] WINDOWS_ROOT_PATH, String key) 
                                             throws Exception {
      Class theClass = Class.forName("java.util.prefs.WindowsPreferences");
 
      int[] result = openKey1(hkey, windowsAbsolutePath(WINDOWS_ROOT_PATH),
                              KEY_QUERY_VALUE);
      if (result[ERROR_CODE] != ERROR_SUCCESS) {
         throw new Exception("Path not found!");
      }
      int nativeHandle = result[NATIVE_HANDLE];
 
      Method m = theClass.getDeclaredMethod("WindowsRegQueryValueEx",
                                            new Class[]{int.class, byte[].class});
      m.setAccessible(true);
      byte[] windowsName = toWindowsName(key);
      Object value = m.invoke(null, new Object[]{new Integer(nativeHandle), windowsName});
      WindowsRegCloseKey(nativeHandle);
      if (value == null) {
         throw new Exception("Path found.  Key not found.");
      }
 
      byte[] origBuffer = (byte[]) value;
      byte[] destBuffer = new byte[origBuffer.length - 1];
      System.arraycopy(origBuffer, 0, destBuffer, 0, origBuffer.length - 1);
 
      return new String(destBuffer);
   }
 
   public static int WindowsRegCloseKey(int nativeHandle) 
                                         throws Exception {
      Class theClass = Class.forName("java.util.prefs.WindowsPreferences");
      Method m = theClass.getDeclaredMethod("WindowsRegCloseKey", new Class[]{int.class});
      m.setAccessible(true);
      Object ret = m.invoke(null, new Object[]{new Integer(nativeHandle)});
      return ((Integer) ret).intValue();
   }
 
   public static int openKey(byte[] windowsAbsolutePath, int securityMask) 
                                         throws Exception {
      Class theClass = Class.forName("java.util.prefs.WindowsPreferences");
      Method m = theClass.getDeclaredMethod("openKey", new Class[]{byte[].class, int.class});
      m.setAccessible(true);
      Object ret = m.invoke(null, new Object[]{windowsAbsolutePath, new Integer(securityMask)});
      return ((Integer) ret).intValue();
   }
 
   public static int[] openKey1(int hkey, byte[] windowsAbsolutePath, int securityMask) 
                                         throws Exception {
      Class theClass = Class.forName("java.util.prefs.WindowsPreferences");
      Method m = theClass.getDeclaredMethod("WindowsRegOpenKey", new Class[]{int.class, 
                                                                             byte[].class, 
                                                                             int.class});
      m.setAccessible(true);
      Object ret = m.invoke(null, new Object[]{new Integer(hkey), 
                                               windowsAbsolutePath, 
                                               new Integer(securityMask)});
      return (int[]) ret;
   }
 
   private static byte[] stringToByteArray(String str) {
      byte[] result = new byte[str.length() + 1];
      for (int i = 0; i < str.length(); i++) {
         result[i] = (byte) str.charAt(i);
      }
      result[str.length()] = 0;
      return result;
   } 
 
   private static byte[] windowsAbsolutePath(byte[] WINDOWS_ROOT_PATH) {
      ByteArrayOutputStream bstream = new ByteArrayOutputStream();
      bstream.write(WINDOWS_ROOT_PATH, 0, WINDOWS_ROOT_PATH.length - 1);
      StringTokenizer tokenizer = new StringTokenizer(absolutePath(), "/");
      while (tokenizer.hasMoreTokens()) {
         bstream.write((byte) '\');
         String nextName = tokenizer.nextToken();
         byte[] windowsNextName = toWindowsName(nextName);
         bstream.write(windowsNextName, 0, windowsNextName.length - 1);
      }
      bstream.write(0);
      return bstream.toByteArray();
   }
 
   private static String absolutePath() {
      return "/";
   }
 
   private static byte[] toWindowsName(String javaName) {
      StringBuffer windowsName = new StringBuffer();
      for (int i = 0; i < javaName.length(); i++) {
         char ch = javaName.charAt(i);
         if ((ch < 0x0020) || (ch > 0x007f)) {
            throw new RuntimeException("Unable to convert to Windows name");
         }
         if (ch == '\') {
            windowsName.append("//");
         } else if (ch == '/') {
            windowsName.append('\');
         } else if ((ch >= 'A') && (ch <= 'Z')) {
            windowsName.append("/" + ch);
         } else {
            windowsName.append(ch);
         }
      }
      return stringToByteArray(windowsName.toString());
   }
 
   private static String toJavaValueString(byte[] windowsNameArray) {
      // Use modified native2ascii algorithm
      String windowsName = byteArrayToString(windowsNameArray);
      StringBuffer javaName = new StringBuffer();
      char ch;
      for (int i = 0; i < windowsName.length(); i++) {
         if ((ch = windowsName.charAt(i)) == '/') {
            char next = ' ';
 
            if (windowsName.length() > i + 1 &&
                  (next = windowsName.charAt(i + 1)) == 'u') {
               if (windowsName.length() < i + 6) {
                  break;
               } else {
                  ch = (char) Integer.parseInt
                        (windowsName.substring(i + 2, i + 6), 16);
                  i += 5;
               }
            } else
                  if ((windowsName.length() > i + 1) &&
                  ((windowsName.charAt(i + 1)) >= 'A') && (next <= 'Z')) {
               ch = next;
               i++;
            } else if ((windowsName.length() > i + 1) &&
                  (next == '/')) {
               ch = '\';
               i++;
            }
         } else if (ch == '\') {
            ch = '/';
         }
         javaName.append(ch);
      }
      return javaName.toString();
   }
 
   private static String byteArrayToString(byte[] array) {
      StringBuffer result = new StringBuffer();
      for (int i = 0; i < array.length - 1; i++) {
         result.append((char) array[i]);
      }
      return result.toString();
   }
}

Tokenizing text with a StreamTokenizer

Instantiate a StreamTokenizer, pass it a Reader instance and loop through the available tokens with nextToken. This method returns an integer that refers to the type of token that was read. These are the possibilities:

   TT_EOF		end of file
   TT_EOL		end of line
   TT_NUMBER	numeric value, the actual value is stored in nval
   TT_WORD		word value, the actual value is stored in sval
   "			a quoted string, the actual value is stored in sval
   x			a character token, x replaced by the character converted to an int

This simple example shows you how to read in a text file and print out its tokens.

Main.java:

import java.io.*;
 
public class Main
{
   public static void main(String []args) throws Exception{
      if (args.length != 1) {
         System.out.println("Usage: java Main <file>");
         System.exit(1);
      }
 
      BufferedReader br = new BufferedReader(new FileReader(args[0]));
      StreamTokenizer st = new StreamTokenizer(br);
 
      int t = st.nextToken();
      while (t != StreamTokenizer.TT_EOF) {
         switch(t) {
            case StreamTokenizer.TT_EOL:
               System.out.println("TT_EOL");
               break;
            case StreamTokenizer.TT_NUMBER:
               System.out.println("TT_NUMBER: " + st.nval);
               break;
            case StreamTokenizer.TT_WORD:
               System.out.println("TT_WORD: " + st.sval);
               break;
            case '"':
               System.out.println("quoted string: " + st.sval);
               break;
            default:
               System.out.println("tokentype: " + (char) t);
         }
  
         t = st.nextToken();
      }
   }
}

If we run it on the following text file:

/* 
 * simple program in Java
 */
 
public class Main {
   public static void Main(String []args) {
      // make calculation
      int a = 4 / 2;
 
      System.out.println("result: " + a);
   }
}

it produces the following result.

tokentype: *
TT_WORD: simple
TT_WORD: program
TT_WORD: in
TT_WORD: Java
tokentype: *
TT_WORD: public
TT_WORD: class
TT_WORD: Main
tokentype: {
TT_WORD: public
TT_WORD: static
TT_WORD: void
TT_WORD: Main
tokentype: (
TT_WORD: String
tokentype: [
tokentype: ]
TT_WORD: args
tokentype: )
tokentype: {
TT_WORD: int
TT_WORD: a
tokentype: =
TT_NUMBER: 4.0
TT_WORD: System.out.println
tokentype: (
quoted string: result: 
tokentype: +
TT_WORD: a
tokentype: )
tokentype: ;
tokentype: }
tokentype: }

Notice that /* , / , // and whitespace seem to be left out! In addition, anything that comes after a / is left out too! The reason for this is that StreamTokenizer has a initial setup:

   - 'A' to 'Z' and 'a' to 'z' and u00A0 till u00FF
	are considered wordchars
   - u0000 till u0020 is considered whitespace
   - / is a comment character
   - ' and " are considered quote characters
   - Numbers are parsed (notice 4 has become 4.0)
   - EOL is considered whitespace
   - C/C++ comments are not recognized.

You can customize the StreamTokenizer in a number of ways:

1. wordChars(int lo, int hi)

The lo and hi parameters specify the unicode range of characters that you would like to see treated as part of a word. You can call this method several times to include several ranges. Try this after you have instantiated the StreamTokenizer:

      // consider all values in the range '{' and '}' as whitespace
      st.wordChars('{', '}');

2. whitespaceChars(int lo, int hi)

The lo and hi parameters specify the unicode range of characters that you would like to see treated as whitespace. You can call this method several times to include several ranges. Try this:

      // consider all values in the range '{' and '}' as whitespace
      st.whitespaceChars('{', '}'); 

3. ordinaryChars(int lo, int hi)

The lo and hi parameters specify the unicode range of characters that you would like to see treated as being an ordinary character, meaning it’s not part of a word, number, whitespace, etc. It will be returned by nextToken as a single character. There’s a variation on this method that takes only one parameter. Try this:

      // consider all values in the range 'a' to 'g' as ordinary char
      st.ordinaryChars('a', 'g');

4. commentChar(int ch)

Specifies that the value ch should be treated as a comment character, meaning the character plus the rest of the line is ignored. Try this:

      // treat 'p' as being a comment
      st.commentChar('p');

5. quoteChar(int ch)

Tells the tokenizer that all characters between this delimiter ch are treated as a string constant. Try this:

      st.quoteChar('/');

6. parseNumbers

This tells the tokenizer that characters from 0 to 9, the period and the minus sign should be recognized as being part of a TT_NUMBER token, if it can be constructed. By default, parseNumbers is set. You can have . and – treated otherwise but then you would have to use the methods ordinaryChar or wordChars.

7. eolIsSignificant(boolean b)

If b is set, TT_EOL will be returned whenever an end-of-line is encountered. Otherwise, they are ignored. Try this:

      st.eolIsSignificant(true);

8. slashStarComments(boolean b)

If b is set, all characters between /* and */ are ignored (C style comments)

9. slashSlashComments(boolean b)

If b is set, // is recognized as being comments (the rest of the line is ignored). (C++ style comments)

10. lowerCaseMode(boolean lc)

if lc is set, all word tokens are lowercased when returned.

11. pushBack()

“Pushes” the last token that was returned back on the stream. Next time nextToken is invoked, the same token will be returned as the last one.

Then there’s another member variable lineno that you may invoke at any time to get the current linenumber.

Using the BorderFactory in Swing

If you have lots of components with borders in your Swing GUI, you could create a Border instance for every component. The BorderFactory class provides a way to share border instances among different components. When using the class for the first time, it creates a number of static borders that it reuses and returns when you call one of the static create methods that the class exposes.

Main.java:

import javax.swing.border.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
 
public class Main extends JFrame
{
   public Main() {
      getContentPane().setLayout(new FlowLayout(FlowLayout.LEFT));
 
      JLabel label1 = new JLabel("Label 1");
      label1.setBorder(BorderFactory.createLoweredBevelBorder()); 
 
      // reuse border
      JLabel label2 = new JLabel("Label 2");
      label2.setBorder(BorderFactory.createLoweredBevelBorder()); 
 
      getContentPane().add(label1);
      getContentPane().add(label2);
      
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(0);
         }
      });
   }
 
   public static void main(String [] args) {
      Main main = new Main();
      main.pack();
      main.setVisible(true);
   } 
} 

Maximizing a JFrame

This is as close as you can get: just resize the window to the current screensize:

 
import javax.swing.*;
import java.awt.*;
   
public class Maximize extends JFrame {
 
   public Maximize() {
      Toolkit tk = getToolkit();
      
      setSize((int) getToolkit().getScreenSize().getWidth(), 
              (int) getToolkit().getScreenSize().getHeight());
   }
 
   public static void main(String args[]) {
      new Maximize().setVisible(true);     
   }
}

Scrolling a JScrollPane to the bottom programmatically

Call the method scrollRectToVisible of the component that is added to the JScrollPane container as shown in following example:

Main.java:

import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
 
public class Main extends JFrame
{
   public Main() throws Exception {
      getContentPane().setLayout(new BorderLayout());
 
      final JPanel panel = createPanel(); 
      final JScrollPane scrollpane = new JScrollPane(panel);
 
      JButton button = new JButton("Scroll to bottom!");
      button.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent ae) {
            panel.scrollRectToVisible(
              new Rectangle(0, panel.getHeight()-1, 1, 1));
         }
      });
 
      getContentPane().add(BorderLayout.NORTH, button);
      getContentPane().add(BorderLayout.CENTER, scrollpane);
 
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(0);
         }
      });
   }
 
   public static JPanel createPanel() throws Exception {
      JPanel panel = new JPanel();
      panel.setLayout(new GridLayout(50, 20, 10, 10));
 
      for (int i=0; i<50; i++) {
         for (int j=0; j<20; j++) {
            JLabel label = new JLabel("label " + i + ", " + j);
            panel.add(label);    
         }
      }
 
      return panel;
   }
 
   public static void main(String [] args) throws Exception  {
      Main main = new Main();
      main.setSize(600, 600);
      main.setVisible(true);
   } 
} 

Programmatically selecting a tab of a JTabbedPane

Use the method setSelectedIndex and pass it the index of the tab that you want to be selected. Here’s an example:

import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import java.awt.*;
import java.net.*;
 
public class Main extends JFrame implements ActionListener {
   JTabbedPane tabbedPane;
   int ntabs = 0;
 
   public Main() {
      getContentPane().setLayout(new BorderLayout());
      tabbedPane = new JTabbedPane();
      createTab();
 
      getContentPane().add(BorderLayout.CENTER, tabbedPane);
      setJMenuBar(createMenuBar());
  
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(0);
         }
      });
   
      setTitle("JTabbedPane Orientation Demonstration");
      setSize(new Dimension(400, 400));
   }
 
   protected JMenuBar createMenuBar() {
      JMenuBar menuBar = new JMenuBar();
 
      JMenu menu1 = new JMenu("JTabbedPane");
      JMenuItem menuItem1 = new JMenuItem("Create new tab");
      menuItem1.addActionListener(this);
      menu1.add(menuItem1);
      JMenu menu2 = new JMenu("Select tab");
      JMenuItem menuItem2 = new JMenuItem("Random tab");
      menuItem2.addActionListener(this);
      menu2.add(menuItem2);
      menuBar.add(menu1);
      menuBar.add(menu2);
 
      return menuBar;
   }
  
   public void actionPerformed(ActionEvent e) {
      if (e.getActionCommand().equals("Create new tab")) {
         createTab();
      }
      else if (e.getActionCommand().equals("Random tab")) {
         selectRandomTab();
      }
   }
 
   protected void createTab() {
      ntabs++;
      tabbedPane.addTab("Tab #" + ntabs, new JLabel("Tab #" + ntabs));
   }
 
   protected void selectRandomTab() {
      Random r = new Random();
      int tabToSelect = r.nextInt(ntabs);
      tabbedPane.setSelectedIndex(tabToSelect);
   }
  
   public static void main(String []args) {
      Main main = new Main();
      main.show();
   }
}

Detecting when JToolBar has been made floatable

Main.java:

import javax.swing.plaf.basic.*;
import javax.swing.border.*;
import java.awt.event.*;
import javax.swing.*;
import java.beans.*;
import java.awt.*;
 
public class Main extends JFrame
{
   public Main()
   {
      final JToolBar toolBar = new JToolBar();
      JButton gifButton = new JButton("GIF");
      JButton jpgButton = new JButton("JPG");
      JButton tiffButton = new JButton("TIFF");
      toolBar.add(gifButton);
      toolBar.add(jpgButton);
      toolBar.add(tiffButton);
 
      getContentPane().add(BorderLayout.NORTH, toolBar);
 
      toolBar.addPropertyChangeListener(new PropertyChangeListener() {
         boolean wasFloating = false;
 
         public void propertyChange(PropertyChangeEvent evt) {
            if (toolBar.getUI() instanceof BasicToolBarUI) {
               BasicToolBarUI bt = (BasicToolBarUI) toolBar.getUI();
               if (wasFloating != bt.isFloating()) {
                  wasFloating = bt.isFloating();
                  System.out.println("Floating: " + wasFloating);
               }
            }
         }
      });            
 
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(0);
         }
      });
   }
      
   public static void main(String []args) {
      Main main = new Main();
      main.setSize(300, 300);
      main.setVisible(true);
   }
}

Setting the background image for a JInternalFrame

Main.java:

import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
import java.net.*;
 
public class Main extends JFrame {
   JDesktopPane desktop;
   int nframes = 0;
 
   public Main() {
      getContentPane().setLayout(new BorderLayout());
      desktop = new JDesktopPane(); 
      createInternalFrame();
      getContentPane().add(BorderLayout.CENTER, desktop);
      setJMenuBar(createMenuBar());
  
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(0);
         }
      });
   
      setTitle("Transparent JInternalFrame Demonstration");
      setSize(new Dimension(400, 400));
   }
 
   protected JMenuBar createMenuBar() {
      JMenuBar menuBar = new JMenuBar();
 
      JMenu menu = new JMenu("JDesktopPane");
      JMenuItem menuItem1 = new JMenuItem("Create new JInternalFrame");
      menuItem1.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent ae) {
            createInternalFrame();
         }
      });
      menu.add(menuItem1);
      menuBar.add(menu);
 
      return menuBar;
   }
 
   protected void createInternalFrame() {
      JInternalFrame frame = new JInternalFrame("InternalFrame", 
         true,    // resizable
         true,    // closable
         true,    // maximizable
         true);   // iconifiable
      frame.setVisible(true); 
      desktop.add(frame);
      frame.setSize(200, 200);
      frame.setLocation(30*nframes, 30*nframes);
      nframes++;
      frame.setContentPane(new ImageBackgroundPanel(
                             new ImageIcon("mong.jpg")));
 
      frame.getContentPane().add(new JLabel("Your name"));
      frame.getContentPane().add(new JTextField(10));
 
      try {
         frame.setSelected(true);
      } catch (java.beans.PropertyVetoException e) {}
   }
 
   public static void main(String []args) {
      Main main = new Main();
      main.show();
   }
}
 
class ImageBackgroundPanel extends JPanel
{
   private ImageIcon bgImage;
 
   public ImageBackgroundPanel(ImageIcon bgImage) {
      this.bgImage = bgImage;
   }
 
   public void paintComponent(Graphics g) {    
      Dimension d = getSize();      
      g.drawImage(bgImage.getImage(), 0, 0, (int)d.getWidth(), (int)d.getHeight() , this);          
   }
}

Creating a rounded JButton

Extend the JButton class and override the paintComponent and paintBorder methods. To ensure that only mouse-clicks are accepted within the round shape, override the contains method too.
Main.java:

import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
import java.awt.*;
   
public class Main extends JFrame implements ActionListener {
   JList list; 
 
   public Main() {
      getContentPane().setLayout(new FlowLayout());
 
      RoundButton rbutton = new RoundButton("Click me!");
      rbutton.addActionListener(this);
      getContentPane().add(BorderLayout.CENTER, rbutton);
  
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent event) {
            System.exit(0);   
         }      
      });
 
      setSize(400, 400);
   }
 
   public void actionPerformed(ActionEvent ae) {
      System.out.println("Your pressed the "" + ae.getActionCommand() + "" button");
   }
   
   public static void main(String[] args) {
      (new Main()).show();
   }
}
 
class RoundButton extends JButton
{
   // for mouse detection purposes
   Shape shape;

   public RoundButton(String label) {
      super(label);
      // allows us to paint a round background
      // if true, it would be rectangular
      setContentAreaFilled(false);
   }
 
   protected void paintComponent(Graphics g) {
      // if the button is pressed and ready to be released
      if (getModel().isArmed()) {
         g.setColor(Color.lightGray);
      } else {
         g.setColor(getBackground());
      }
 
      g.fillOval(0, 0, getSize().width-1, getSize().height-1);
 
      super.paintComponent(g);
   }
 
   // paint a round border as opposed to a rectangular one
   protected void paintBorder(Graphics g) {
      g.setColor(getForeground());
      g.drawOval(0, 0, getSize().width-1, getSize().height-1);
   }
 
   // only clicks within the round shape should be accepted
   public boolean contains(int x, int y) {
      if (shape == null || !shape.getBounds().equals(getBounds())) {
         shape = new Ellipse2D.Float(0, 0, getWidth(), getHeight());
      }
 
      return shape.contains(x, y);
   }
}

Disabling some of the elements in a JComboBox

We wrap a JComboBox element in our ConditionalItem object that includes a boolean variable isEnabled. Now, in order to change the default behavior of the JComboBox, we need to handle two things: first blur out the elements that are disabled using our own renderer and second ensure the disabled elements cannot be selected by adding a ActionListener that selects the old selected element in case a disabled item was selected.

Main.java:

import javax.swing.plaf.basic.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import java.awt.*;
    
public class Main extends JFrame
{ 
   public Main() {
      getContentPane().setLayout(new FlowLayout());
      
      final JComboBox combobox = 
         new JComboBox(
            new ConditionalItem[] { 
               new ConditionalItem("Item 1"),
               new ConditionalItem("Item 2", false),
               new ConditionalItem("Item 3", false),
               new ConditionalItem("Item 4")
            }
         );
 
      getContentPane().add(combobox);
      combobox.setRenderer(new ConditionalComboBoxRenderer());
      combobox.addActionListener(new ConditionalComboBoxListener(combobox));
  
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(1);
         }
      });      
   
      setSize(new Dimension(200, 200));
   } 
 
   public static void main(String[] args) throws Exception {
      Main main = new Main();
      main.setVisible(true);
   }
}
 
class ConditionalComboBoxRenderer extends BasicComboBoxRenderer implements ListCellRenderer
{
   public Component getListCellRendererComponent(JList list, 
           Object value, int index, boolean isSelected, boolean cellHasFocus) {
      if (isSelected) {
        setBackground(list.getSelectionBackground());
        setForeground(list.getSelectionForeground());
      } else {
        setBackground(list.getBackground());
        setForeground(list.getForeground());
      } 
 
      if (!((Conditionable) value).isEnabled()) {
        setBackground(list.getBackground());
        setForeground(UIManager.getColor("Label.disabledForeground"));
      }
 
      setFont(list.getFont());
      setText((value == null) ? "" : value.toString());
      return this;
   }  
}
 
class ConditionalComboBoxListener implements ActionListener {
   JComboBox combobox;
   Object oldItem;
    
   ConditionalComboBoxListener(JComboBox combobox) {
      this.combobox = combobox;
      combobox.setSelectedIndex(0);
      oldItem = combobox.getSelectedItem();
   }
    
   public void actionPerformed(ActionEvent e) {
      Object selectedItem = combobox.getSelectedItem();
      if (!((Conditionable) selectedItem).isEnabled()) {
         combobox.setSelectedItem(oldItem);
      } else {
         oldItem = selectedItem;
      }
   }
}
 
class ConditionalItem implements Conditionable {
   Object  object;
   boolean isEnabled;
    
   ConditionalItem(Object object, boolean isEnabled) {
      this.object = object;
      this.isEnabled = isEnabled;
   }
    
   ConditionalItem(Object object) {
     this(object, true);
   }
   
   public boolean isEnabled() {
      return isEnabled;
   }
    
   public void setEnabled(boolean isEnabled) {
      this.isEnabled = isEnabled;
   }
    
   public String toString() {
      return object.toString();
   }
}
 
interface Conditionable {
   public boolean isEnabled();
   public void setEnabled(boolean enabled);
   public String toString();
}