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?

Tuesday, May 7, 2013

Case Insensitive Map

I use Maps often, especially using a String as the key. Working with database fields, there are times when I wished that String was case insensitive. So I used a couple of LinkedHashMap objects to implement my own...

Usage:

Map<String, Object> myMap = new CaseInsensitiveLinkedHashMap<Object>()
Object o1 = new Object();
myMap.put("id7", o1);
Object o2 = myMap.get("ID7");
System.out.println(o1==o2 ? "success" : "failure"); // prints "success"

// end

It preserves the original capitalization of the keys while maintaining the efficiency of the hash lookups, but the lookups are case insensitive.

Source:
        

package util;

import java.util.*;

/**
 * allows you to create a Map<String, ?> where the String key is case insensitive.
 * @author Jonathan
 */
public final class CaseInsensitiveLinkedHashMap<T> implements Map<String, T> {

    private final Map<String, String> keys;
    private final Map<String, T> values;

    public CaseInsensitiveLinkedHashMap() {
        keys = new LinkedHashMap<String, String>();
        values = new LinkedHashMap<String, T>();
    }

    public CaseInsensitiveLinkedHashMap(int initialCapacity) {
        keys = new LinkedHashMap<String, String>(initialCapacity);
        values = new LinkedHashMap<String, T>(initialCapacity);
    }

    public CaseInsensitiveLinkedHashMap(int initialCapacity, float loadFactor) {
        keys = new LinkedHashMap<String, String>(initialCapacity, loadFactor);
        values = new LinkedHashMap<String, T>(initialCapacity, loadFactor);
    }

    public CaseInsensitiveLinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
        keys = new LinkedHashMap<String, String>(initialCapacity, loadFactor, accessOrder);
        values = new LinkedHashMap<String, T>(initialCapacity, loadFactor, accessOrder);
    }

    public CaseInsensitiveLinkedHashMap(Map<? extends String, ? extends T> initialValues) {
        this(initialValues.size());
        for (Entry<? extends String, ? extends T> entry : initialValues.entrySet()) {
            put(entry.getKey(), entry.getValue());
        }
    }

    @Override
    public boolean containsKey(Object key) {
        return containsKey(key.toString());
    }

    public boolean containsKey(String key) {
        return values.containsKey(key.toLowerCase());
    }

    @Override
    public T get(Object key) {
        return get(key.toString());
    }

    public T get(String key) {
        return values.get(key.toLowerCase());
    }

    @Override
    public T put(String key, T value) {
        final String lowerCaseKey = key.toLowerCase();
        keys.put(lowerCaseKey, key);
        return values.put(lowerCaseKey, value);
    }

    @Override
    public T remove(Object key) {
        return remove(key.toString());
    }

    public T remove(String key) {
        final String lowerCaseKey = key.toLowerCase();
        keys.remove(lowerCaseKey);
        return values.remove(lowerCaseKey);
    }

    @Override
    public void putAll(Map<? extends String, ? extends T> m) {
        for (Entry<? extends String, ? extends T> entry : m.entrySet()) {
            put(entry.getKey(), entry.getValue());
        }
    }

    @Override
    public void clear() {
        keys.clear();
        values.clear();
    }

    @Override
    public Set<String> keySet() {
        return new LinkedHashSet<String>(keys.values());
    }

    @Override
    public Collection<T> values() {
        return values.values();
    }

    @Override
    public Set<Entry<String, T>> entrySet() {
        Set<Entry<String, T>> set = new LinkedHashSet<Entry<String, T>>();
        for (final String keyLowerCase : keys.keySet()) {
            Entry<String, T> entry = new Entry<String, T>() {

                final String key = keys.get(keyLowerCase);

                @Override
                public String getKey() {
                    return key;
                }

                @Override
                public T getValue() {
                    if (containsKey(key)) {
                        return get(key);
                    }
                    return null;
                }

                @Override
                public T setValue(T value) {
                    return put(key, value);
                }

            };
            set.add(entry);
        }
        return set;
    }

    @Override
    public int size() {
        return values.size();
    }

    @Override
    public boolean isEmpty() {
        return values.isEmpty();
    }

    @Override
    public boolean containsValue(Object value) {
        return values.containsValue(value);
    }

}

// end

Thursday, March 14, 2013

Auto Resize Columns in a JTable (Java Swing)

Here's some code to automatically resize columns in a Java Swing JTable, based on the data in that table...


Usage:

        templatesTable.setModel(tableModel);
        UserInterfaceUtility.resizeColumns(templatesTable);



Source:

package ui;

import java.util.ArrayList;
import java.util.List;
import javax.swing.JTable;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;

/**
 * http://javajon.blogspot.com/
 * @author Jonathan
 */
public class UserInterfaceUtility {

    /**
     * resizes the columns in a JTable based on the data in that table. data
     * scanned is limited to the first 25 rows.
     *
     * @param table the table with the columns to resize
     */
    public static void resizeColumns(JTable table) {

        final TableModel model = table.getModel();
        final int columnCount = model.getColumnCount();
        final int rowCount = model.getRowCount();
        final List charactersPerColumn = new ArrayList();

        // initiazlize character counts
        final Integer integerZero = Integer.valueOf(0);
        for (int col = 0; col < columnCount; col++) {
            charactersPerColumn.add(integerZero);
        }

        // scan first 25 rows
        final int rowsToScan = (rowCount < 25) ? rowCount : 25;
        for (int row = 0; row < rowsToScan; row++) {
            for (int col = 0; col < columnCount; col++) {

                // character counts for comparison
                final int existingCharacterCount = charactersPerColumn.get(col).intValue();
                final Object cellValue = model.getValueAt(row, col);
                if (cellValue != null) {
                    final Integer newCharacterCount = Integer.valueOf(cellValue.toString().length());

                    // do we need to increase the character count?
                    if (newCharacterCount.intValue() > existingCharacterCount) {
                        charactersPerColumn.set(col, newCharacterCount);
                    }
                }

            }
        }

        // prepare the table for column resizing
        final TableColumnModel columnModel = table.getColumnModel();
        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        
        // set maximum character counts
        final Integer maximumCharacterCount = Integer.valueOf(24);
        for (int col = 0; col < columnCount; col++) {
            final int existingCharacterCount = charactersPerColumn.get(col).intValue();
            if (existingCharacterCount > maximumCharacterCount.intValue()) {
                charactersPerColumn.set(col, maximumCharacterCount);
            }
        }

        // set column widths
        for (int col = 0; col < columnCount; col++) {
            final int existingCharacterCount = charactersPerColumn.get(col).intValue();
            final int columnWidth = 18 + (existingCharacterCount * 7);
            columnModel.getColumn(col).setPreferredWidth(columnWidth);
        }        
        
    }
}

Saturday, January 26, 2013

Calling Java 7 from Cold Fusion

I tried calling Java 7 objects from Cold Fusion10  today and I got nothing ...  Literally, nothing.  At the CFOBJECT tag, the script would just die.  No error message.  Nothing in the log files.  Just nothing.

I made a Java 7 object, one class, one method, nothing special...Same results.  Then I converted that project (one class) to JDK 6, and poof!  The same object compiled using JDK6 works fine! 

http://blogs.coldfusion.com/post.cfm/java-7-support-for-coldfusion

The above article states that CF 9 and 10 will support Java 7 through updates by February 2013 ... we'll we're getting close.  I just downloaded all the updates and ...

Fail!

Even with all updates applied, the script just dies when i try to call Java 7 objects from CF 10.  So now I've got all of this code written for JDK7, and I need to call it from CF10 ... What to do ...  Looks like I might be converting a lot of code written for JDK7 to work with JDK6, which sucks, because I like try-with-resources, and I like the diamond operator ... a lot :(