/* * Dynamic GUI example, builds GUI's based on given Java bean * Originally developed as part of dissertation project (mobile servicing application, part) * The University Of Liverpool, UK */ package dynamicguiexample; import java.awt.Component; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; /** * Class to build a dynamic GIU for the use of editing bean objects * given a bean object it is inspected and a simple editor is created * that can edit the beans properties. * @author Paul Carey (info<@at@>paulcarey.co.uk) */ public class DynamicGui extends JFrame { /** * Holds a bean info object for the bean passed in */ private BeanInfo info; /** * Holds a collection of properties of the passed bean */ private PropertyDescriptor[] props; /** * The bean object beaing edited */ private Object o; /** * Holds a title */ private String title; /** * Holds a mapping of properties to there GUI control components */ private HashMap propertyComponents = new HashMap(); /** * The main panel that will display the components */ private JPanel panel = new JPanel(); /** * Quick debug flag to out debug info */ private boolean debug = true; /** * Quick flag to turn reading of properties on or off during the build process */ private boolean readProps = true; /** * Constructor takes as an argument the bean object to be edited * @param o The object to edit */ public DynamicGui(Object o) { this.o = o; build(); } /** * Constructor takes as arguments the bean to be edited and a title * @param o The object to edit * @param title The title to display (e.g: report title) */ public DynamicGui(Object o, String title) { this.o = o; this.title = title; build(); } /** * Inspects the passed bean building the collection of properties */ private void inspect() { try { info = Introspector.getBeanInfo(getO().getClass()); props = info.getPropertyDescriptors(); } catch (IntrospectionException ex) { Logger.getLogger(DynamicGui.class.getName()).log(Level.SEVERE, null, ex); } } /** * Builds the GUI based on the passed beans properties */ private void build() { //inspect the passed beans properties first inspect(); //set the panel to use a simple grid layout, so we can have a label and then component side by side panel.setLayout(new GridLayout(0, 2)); //if a title was passed in add a title to top of form if (title != null) { //add title JLabel panel.add(new JLabel(title)); //add empty JLabel to fill second space panel.add(new JLabel(" ")); } //loop the array of properties building the form for (PropertyDescriptor p : props) { //only add the property to the gui if it can be edited if (p.getWriteMethod() != null) { //determine and create a component to edit this property Class type = p.getPropertyType(); //create a label for this property panel.add(getPropLabel(p)); if (type == String.class || type == Integer.class) { JTextField txtField = new JTextField(); panel.add(txtField); propertyComponents.put(p, txtField); } } } //Add the save and read buttons JButton btnSave = new JButton("Save"); btnSave.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { writeProps(); } }); JButton btnRead = new JButton("Read"); panel.add(btnSave); panel.add(btnRead); btnRead.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { readProps(); } }); //if the readProps flag is true then read the passed values onto the form if (readProps) { readProps(); } //add everything to a scroll pane (helpful for mobile use, as was intended) JScrollPane scroll = new JScrollPane(panel); add(scroll); pack(); } /** * Returns a formated label to be used on the GUI, converts from camelCase to * Title Case * @param p The property to create a label for * @return A correctly formated label */ private JLabel getPropLabel(PropertyDescriptor p) { //Create an efficient string builder StringBuilder sb = new StringBuilder(); //Quick int count so we know if we are on first char int i = 0; for (Character c : p.getDisplayName().toCharArray()) { //if first char then capitalize it if (i == 0) { c = Character.toUpperCase(c); sb.append(c); } //if we find a capital then insert a space before it else if (Character.isUpperCase(c)) { sb.append(" " + c); } //Otherwise just add the char to the builder else { sb.append(c); } i++; } //Return the label return new JLabel(sb.toString()); } /** * Reads properties from beans and into there associated GUI components */ private void readProps() { //loop over the keyset one method at a time for (PropertyDescriptor p : propertyComponents.keySet()) { Component c = propertyComponents.get(p); if (c instanceof JTextField) { try { JTextField txtField = (JTextField) c; Method getter = p.getReadMethod(); Object value = getter.invoke(o, null); if (value == null) { debugOut("value null"); } else { txtField.setText(value.toString()); } } catch (IllegalAccessException ex) { Logger.getLogger(DynamicGui.class.getName()).log(Level.SEVERE, null, ex); } catch (IllegalArgumentException ex) { Logger.getLogger(DynamicGui.class.getName()).log(Level.SEVERE, null, ex); } catch (InvocationTargetException ex) { Logger.getLogger(DynamicGui.class.getName()).log(Level.SEVERE, null, ex); } } } } /** * Writes properties from there GUI components and back into the master object */ private void writeProps() { //loop over the keyset one property at a time for (PropertyDescriptor p : propertyComponents.keySet()) { //Debug debugOut("saving prop " + p.getName()); //Get the component responsible for this property Component c = propertyComponents.get(p); //See what type of component we are dealing with if (c instanceof JTextField) { //case component to a text field JTextField txtField = (JTextField) c; //get the text from the field String value = ((JTextField) c).getText(); //try to write the value back to the object try { //Get the property type, so we know how to deal with it Class type = p.getPropertyType(); //type is a string if (type == String.class) { //Debug messages if (p.getWriteMethod() == null) { debugOut("write method null"); } if (getO() == null) { debugOut("o is null"); } if (txtField.getText() == null) { debugOut("text field is null"); } //Invoke the properties write method on the object p.getWriteMethod().invoke(getO(), value); } //type is an integer so convert string to integer else if (type == Integer.class) { if (value.length() > 0) { p.getWriteMethod().invoke(getO(), Integer.valueOf(value)); } } } catch (IllegalAccessException ex) { Logger.getLogger(DynamicGui.class.getName()).log(Level.SEVERE, null, ex); } catch (IllegalArgumentException ex) { Logger.getLogger(DynamicGui.class.getName()).log(Level.SEVERE, null, ex); } catch (InvocationTargetException ex) { Logger.getLogger(DynamicGui.class.getName()).log(Level.SEVERE, null, ex); } } } //If debug flag on then output the results of the save if (debug) { for (PropertyDescriptor p : propertyComponents.keySet()) { try { System.out.println(p.getReadMethod().invoke(getO(), null).toString()); } catch (IllegalAccessException ex) { Logger.getLogger(DynamicGui.class.getName()).log(Level.SEVERE, null, ex); } catch (IllegalArgumentException ex) { Logger.getLogger(DynamicGui.class.getName()).log(Level.SEVERE, null, ex); } catch (InvocationTargetException ex) { Logger.getLogger(DynamicGui.class.getName()).log(Level.SEVERE, null, ex); } } } } /** * Returns the master object being edited * @return the o */ public Object getO() { return o; } /** * Outputs debug messages to standard out * @param str Message to output */ private void debugOut(String str) { if (debug) { System.out.println(str); } } public static void main(String[] args) { Company c = new Company(); c.setCompanyId(123); c.setName("Joe Bloggs Bakers"); new DynamicGui(c, "Example").setVisible(true); } }