Tuesday, July 23, 2013

Java AWT/Swing getComponentVariableName(component)

Sometimes you have a component that you know almost nothing about, and it'd be nice to get the name of it, somehow, but since I haven't taken the time to explicitly name every one of my components, I tried something else. Here's how to use it ...


    private void genericErrorHandler(java.awt.event.AWTEvent evt) {

        String componentName = Awt2.getComponentVariableName(evt.getSource());
        System.out.println(componentName);

    }



Here's the code to make the above code work...


import java.awt.Component;
import java.lang.reflect.Field;

/**
 * additional utilities for working with AWT/Swing.
 * this is a single method for demo purposes.
 * recommended to be combined into a single class
 * module with other similar methods,
 * e.g. MySwingUtilities
 *
 * @author http://javajon.blogspot.com/2013/07/java-awtswing-getcomponentvariablenamec.html
 */
public class Awt2 {

    /**
     * substitute for component.getName() when used in NetBeans or other IDE
     * that creates class fields to hold the components. uses reflection to
     * search through class fields for a match.
     * @param component the component to look for
     * @return hopefully the variable name used to hold this component
     */
    static public String getComponentVariableName(Object object) {

        if (object instanceof Component) {
            final Component component = (Component) object;
            final StringBuilder sb = new StringBuilder();

            // find the form where the variable name would be likely to exist
            final Component parentForm = getParentForm(component);

            // loop through all of the class fields on that form
            for (Field field : parentForm.getClass().getDeclaredFields()) {

                try {
                    // let us look at private fields, please
                    field.setAccessible(true);

                    // get a potential match
                    final Object potentialMatch = field.get(parentForm);

                    // compare it
                    if (potentialMatch == component) {

                        // return the name of the variable used
                        // to hold this component
                        if (sb.length() > 0) sb.append(",");
                        sb.append(field.getName());
                    }

                } catch (SecurityException | IllegalArgumentException 
                        | IllegalAccessException ex) {

                    // ignore exceptions
                }
            }

            if (sb.length() > 0) {
                return sb.toString();
            }
        }

        // if we get here, we're probably trying to find the form
        // itself, in which case it may be more useful to print
        // the class name (MyJFrame) than the AWT-assigned name
        // of the form (frame0)
        final String className = object.getClass().getName();
        final String[] split = className.split("\\.");
        final int lastIndex = split.length - 1;
        return (lastIndex >= 0) ? split[lastIndex] : className;

    }

    /**
     * traverses up the component tree to find the top, which i assume is the
     * dialog or frame upon which this component lives.
     * @param sourceComponent
     * @return top level parent component
     */
    static public Component getParentForm(Component sourceComponent) {
        while (sourceComponent.getParent() != null) {
            sourceComponent = sourceComponent.getParent();
        }
        return sourceComponent;
    }
}



Using the above code, here's an example of a global AWT/Swing event catcher that just dumps everything to the system console...


    Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {

        @Override
        public void eventDispatched(AWTEvent event) {
            System.out.print(Awt2.getComponentVariableName(event.getSource()));
            System.out.print(": ");
            System.out.print(event.paramString());
            System.out.println();
        }
    }, -1L);

Java AWT/Swing getComponentByName(window, name)

C# offers a Controls collection, so you can access controls by name using form.Controls["controlName"]. Java/Swing does not appear to have any such functionality, but here's what I'd like to be able to do ...


    // get a button (or other component) by name
    JButton button = Awt1.getComponentByName(someOtherFrame, "jButton1");

    // do something useful with it (like toggle it's enabled state)
    button.setEnabled(!button.isEnabled());



So, you know ... here's the code to make it possible ...

import java.awt.Component;
import java.awt.Window;
import java.lang.reflect.Field;

/**
 * additional utilities for working with AWT/Swing.
 * this is a single method for demo purposes.
 * recommended to be combined into a single class
 * module with other similar methods,
 * e.g. MySwingUtilities
 * 
 * @author http://javajon.blogspot.com/2013/07/java-awtswing-getcomponentbynamewindow.html
 */
public class Awt1 {

    /**
     * attempts to retrieve a component from a JFrame or JDialog using the name
     * of the private variable that NetBeans (or other IDE) created to refer to
     * it in code.
     * @param <T> Generics allow easier casting from the calling side.
     * @param window JFrame or JDialog containing component
     * @param name name of the private field variable, case sensitive
     * @return null if no match, otherwise a component.
     */
    @SuppressWarnings("unchecked")
    static public <T extends Component> T getComponentByName(Window window, String name) {

        // loop through all of the class fields on that form
        for (Field field : window.getClass().getDeclaredFields()) {

            try {
                // let us look at private fields, please
                field.setAccessible(true);

                // compare the variable name to the name passed in
                if (name.equals(field.getName())) {

                    // get a potential match (assuming correct <T>ype)
                    final Object potentialMatch = field.get(window);

                    // cast and return the component
                    return (T) potentialMatch;
                }

            } catch (SecurityException | IllegalArgumentException 
                    | IllegalAccessException ex) {

                // ignore exceptions
            }

        }

        // no match found
        return null;
    }

}

Monday, July 22, 2013

Calling Overridable Methods from the Constructor

NetBeans will warn you not to call an overridable method from your constructor, and for good reason ...

public class PlayerCharacter implements DailyCycle {

    private static final int MAX_MOVES_REMAINING = 10;
    private static final int NEW_MOVES_PER_DAY = 3;

    private int movesRemaining;

    public PlayerCharacter() {
        newDay(); // WARNING
    }

    @Override
    public void newDay() {
        movesRemaining += NEW_MOVES_PER_DAY;
        if (movesRemaining > MAX_MOVES_REMAINING) {
            movesRemaining = MAX_MOVES_REMAINING;
        }
    }

}



What I Do...

I'm not really sure how other people handle this, but like to make a local version of the method (CTRL-SHIFT-M in NetBeans)...

 

public class PlayerCharacter implements DailyCycle {

    private static final int MAX_MOVES_REMAINING = 10;
    private static final int NEW_MOVES_PER_DAY = 3;

    private int movesRemaining;

    public PlayerCharacter() {
        newDayLocal();
    }

    @Override
    public void newDay() {
        newDayLocal();
    }

    private void newDayLocal() {
        movesRemaining += NEW_MOVES_PER_DAY;
        if (movesRemaining > MAX_MOVES_REMAINING) {
            movesRemaining = MAX_MOVES_REMAINING;
        }
    }

}

This not only solves the warning, but actually solves the problem. The object is no longer allowing subclasses to inject out-of-sequence code into the constructor by overriding a method.

 How do other people handle this? Is there a standard pattern?