Java Tutorial/Trail: The Reflection API

The Reflection API

edit

The Reflection API allows to analyze the structure of the classes composing the running program from inside the program and to access the data contained in those classes. The API consists of the following classes:

Examples

edit

JRMLWriter

edit

The class JRMLWriter is an example of a custom class that uses reflection in order to write an XML dump of a hierarchy of objects into a buffer, a stream or to a network socket. In case of a socket this allows to implement a protocol much like RMI but using XML-formatted data. JRML is used as an abbreviation for "Java Remote Method Invocation Language" in reference to RMI's Java Remote Method Protocol.

The method getAllFields, for instance, iterates over a class and its super classes, retrieving the declared fields of the classes, building a flat array of all fields, changing their accessibility to "true" on the way, because private, protected and package access fields may by default be inaccessible even to reflection.

    /**
     * @param cl Class
     * @return Field[]
     */
    public Field[] getAllFields (Class cl)
    {
        ArrayList<Field> allFields = new ArrayList<Field> ();
        Field[] fields;

        do
        {
            fields = cl.getDeclaredFields ();
            for (int i = 0; i < fields.length; i++)
            {
                if ((fields[i].getModifiers () & getSerializationModifierMask ()) != 0)
                    continue;
                if (!fields[i].isAccessible ())
                    fields[i].setAccessible (true);
                allFields.add (fields[i]);
            }
        }
        while ((cl = cl.getSuperclass ()) != null);

        return (Field[]) allFields.toArray (fields);
    }

(source code from vsrs.svn.sourceforge.net)

SwingFactory

edit

For everybody who wondered why Swing has "Pluggable Look & Feel" packages that do a lot of amazing (well at least a lot of) stuff to modify the design and behavior of the unmodified Swing classes instead of using interfaces — here is SwingFactory. My personal theory is that the Swing authors made a little joke by putting polymorphism "on the wrong side" of Swing. SwingFactory is an unfinished sketch to address the issue; the exercise is to generate widget interfaces using the Reflection API (they merely contain all the public methods in their counterparts as abstract interface methods) and to write an InvocationHandler to redirect the calls to an actual Swing implementation, possibly residing in a different virtual machine. The exercise requires some more work to address the issue of non-widgets required to configure the components properly in order to be actually useful. A look at com.sun.j3d.exp.swing.JCanvas3D may also be interesting.

package org.wikiversity.java_tutorial;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;

public final class SwingFactory
{
	public enum ProxyWidgets {
		JCOLORCHOOSER (),
		JDESKTOPPANE (),
		JFILECHOOSER (),
		JFORMATTEDTEXTFIELD (),
		JOPTIONPANE (),
		JSPINNER (),
		JTEXTPANE (),
		JRADIOBUTTON (),
		JRADIOBUTTONMENUITEM (),
		JSLIDER (),
		JTOOLTIP (),
		DEFAULTTREECELLRENDERER (),
		JTREE (),
		DEFAULTTABLECELLRENDERER (),
		JTABLEHEADER (),
		JPROGRESSBAR (),
		JCHECKBOX (),
		JTABBEDPANE (),
		JTABLE (),
		JEDITORPANE (),
		JTEXTAREA (),
		JVIEWPORT (),
		JSCROLLPANE (),
		DEFAULTLISTCELLRENDERER (),
		JLIST (),
		JSCROLLBAR (),
		JCOMBOBOX (),
		JSEPARATOR (),
		JPOPUPMENU (),
		JMENUBAR (),
		BOX (),
		JTOGGLEBUTTON (),
		JSPLITPANE (),
		JTOOLBAR (),
		JBUTTON (),
		JCHECKBOXMENUITEM (),
		JMENUITEM (),
		JMENU (),
		JINTERNALFRAME (),
		JLAYEREDPANE (),
		JTEXTFIELD (),
		JPASSWORDFIELD (),
		JPANEL (),
		JROOTPANE (),
		JLABEL ();
	}
	
	private final static Class<?>[] SWING_PROXY_INTERFACES = {
		org.wikiversity.java_tutorial.JColorChooser.class, 
		org.wikiversity.java_tutorial.JDesktopPane.class, 
		org.wikiversity.java_tutorial.JFileChooser.class, 
		org.wikiversity.java_tutorial.JFormattedTextField.class, 
		org.wikiversity.java_tutorial.JOptionPane.class, 
		org.wikiversity.java_tutorial.JSpinner.class, 
		org.wikiversity.java_tutorial.JTextPane.class, 
		org.wikiversity.java_tutorial.JRadioButton.class, 
		org.wikiversity.java_tutorial.JRadioButtonMenuItem.class, 
		org.wikiversity.java_tutorial.JSlider.class, 
		org.wikiversity.java_tutorial.JToolTip.class, 
		org.wikiversity.java_tutorial.tree.DefaultTreeCellRenderer.class, 
		org.wikiversity.java_tutorial.JTree.class, 
		org.wikiversity.java_tutorial.table.DefaultTableCellRenderer.class, 
		org.wikiversity.java_tutorial.table.JTableHeader.class, 
		org.wikiversity.java_tutorial.JProgressBar.class, 
		org.wikiversity.java_tutorial.JCheckBox.class, 
		org.wikiversity.java_tutorial.JTabbedPane.class, 
		org.wikiversity.java_tutorial.JTable.class, 
		org.wikiversity.java_tutorial.JEditorPane.class, 
		org.wikiversity.java_tutorial.JTextArea.class, 
		org.wikiversity.java_tutorial.JViewport.class, 
		org.wikiversity.java_tutorial.JScrollPane.class, 
		org.wikiversity.java_tutorial.DefaultListCellRenderer.class, 
		org.wikiversity.java_tutorial.JList.class, 
		org.wikiversity.java_tutorial.JScrollBar.class, 
		org.wikiversity.java_tutorial.JComboBox.class, 
		org.wikiversity.java_tutorial.JSeparator.class, 
		org.wikiversity.java_tutorial.JPopupMenu.class, 
		org.wikiversity.java_tutorial.JMenuBar.class, 
		org.wikiversity.java_tutorial.Box.class, 
		org.wikiversity.java_tutorial.JToggleButton.class, 
		org.wikiversity.java_tutorial.JSplitPane.class, 
		org.wikiversity.java_tutorial.JToolBar.class, 
		org.wikiversity.java_tutorial.JButton.class, 
		org.wikiversity.java_tutorial.JCheckBoxMenuItem.class, 
		org.wikiversity.java_tutorial.JMenuItem.class, 
		org.wikiversity.java_tutorial.JMenu.class, 
		org.wikiversity.java_tutorial.JInternalFrame.class, 
		org.wikiversity.java_tutorial.JLayeredPane.class, 
		org.wikiversity.java_tutorial.JTextField.class, 
		org.wikiversity.java_tutorial.JPasswordField.class, 
		org.wikiversity.java_tutorial.JPanel.class, 
		org.wikiversity.java_tutorial.JRootPane.class, 
		org.wikiversity.java_tutorial.JLabel.class, 
	};

	private InvocationHandler invocationHandler;
	
	private SwingFactory ()
	{ /* nothing to do */ }

	private void setInvocationHandler (InvocationHandler handler)
	{
		invocationHandler = handler;
	}
	
	public final static ArrayList<Class<?>> getProxyInterfaces ()
	{
		return new ArrayList<Class<?>> (Arrays.asList (SWING_PROXY_INTERFACES));
	}

	private final static SwingFactory defaultFactory = new SwingFactory ();
	
	public final static SwingFactory getFactoryInstance ()
	{
		return defaultFactory;
	}

	public final static SwingFactory getFactoryInstance (InvocationHandler invocationHandler)
	{
		SwingFactory factory = new SwingFactory ();
		factory.setInvocationHandler (invocationHandler);
		return factory;
	}

	public final ProxyWidget getWidgetInstance (ProxyWidgets widget)
	{
		try {
			Class<?> proxyIfc    = SWING_PROXY_INTERFACES[widget.ordinal ()];
			Class<?> widgetClass = Proxy.getProxyClass (proxyIfc.getClassLoader (), new Class[] { proxyIfc });
			return (ProxyWidget) widgetClass.
		         getConstructor (new Class[] { InvocationHandler.class }).
		         newInstance (new Object[] { invocationHandler });
		}
		catch (NoSuchMethodException ex)     { fail (ex); }
		catch (InvocationTargetException ex) { fail (ex); }
		catch (IllegalAccessException ex)    { fail (ex); }
		catch (InstantiationException ex)    { fail (ex); }
		return null; // can't happen
	}

	private void fail (Throwable t) {
		t.printStackTrace ();
		System.exit (1);
	}
}
edit