Package net.megabyte.nosaf

Source Code of net.megabyte.nosaf.MBAction

/*
* Copyright (c) 2012 Megabyte Limited - www.megabyte.net
* Distributed under the LGPL license.
*/
package net.megabyte.nosaf;

import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.KeyStroke;
import org.jdesktop.application.Action;
import org.jdesktop.application.PublicMnemonicText;

/**
* This is a concrete Action class that reads its settings from a resource
* bundle in the same format used by SAF.
*
* @author rac
*/
public class MBAction extends AbstractAction {

    private final static Logger log = Logger.getLogger(MBAction.class.getName());

    public static final String TEXT_KEY = "Text";
    public static final String CODE_KEY = "Code";
    public static final String FORM_KEY = "Form";
    public static final String RESB_KEY = "ResourceBundle";
    public static final String PROP_KEY = "Properties";

    private Method actionMethod;

    private ClassLoader classLoader;

    /**
     * Property change listener to propagate the enabled property.
     */
    private PropertyChangeListener enabledListener = new PropertyChangeListener() {
        public void propertyChange(PropertyChangeEvent evt) {
            MBAction.this.setEnabled( (Boolean)evt.getNewValue() );
        }
    };
    /**
     * Property change listener to propagate the selected property.
     */
    private PropertyChangeListener selectedListener = new PropertyChangeListener() {
        public void propertyChange(PropertyChangeEvent evt) {
            MBAction.this.setSelected( (Boolean)evt.getNewValue() );
        }
    };

    // <editor-fold defaultstate="collapsed" desc=" Property: trace ">
    private StringBuilder trace = new StringBuilder();

    /**
     * Get the trace
     */
    public String getTrace() {
        return trace.toString();
    }

    /**
     * Add a message to the trace
     */
    private void trace(String newValue, Object... args) {
        String old = getTrace();
        final String msg = String.format(newValue,args);
        System.err.println(msg);
        this.trace.append(msg).append("\n");
        firePropertyChange("errors", old, getTrace());
    }// </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Property: form ">
    /**
     * Get the "form" - the swing component being designed
     *
     * @return the value of form
     */
    public Object getForm() {
        return getValue(FORM_KEY);
    }

    /**
     * Set the "form" - the swing component being designed
     *
     * @param newValue new value of form
     */
    public void setForm(Object newValue) {
        putValue(FORM_KEY, newValue);
    }// </editor-fold>
    // <editor-fold defaultstate="collapsed" desc=" Property: resourceBundle ">
    private ResourceBundle resourceBundle;

    /**
     * Get the value of resourceBundle
     *
     * @return the value of resourceBundle
     */
    public ResourceBundle getResourceBundle() {
        return resourceBundle;
    }

    /**
     * Set the value of resourceBundle
     *
     * @param newValue new value of resourceBundle
     */
    public void setResourceBundle(ResourceBundle newValue) {
        internalSetResourceBundle(newValue);
        init();
    }

    /**
     * Set the value of resourceBundle (does not call init)
     *
     * @param newValue new value of resourceBundle
     */
    public void internalSetResourceBundle(ResourceBundle newValue) {
        if(newValue==null) return;
        ResourceBundle oldValue = this.resourceBundle;
        this.resourceBundle = newValue;
        firePropertyChange("resourceBundle", oldValue, newValue); //NOI18N
    }

    // </editor-fold>
    // <editor-fold defaultstate="collapsed" desc=" Property: resourceBundleName ">
    /**
     * Loads and sets the specified resource bundle, UNLESS the resource bundle has already been set.
     *
     * @param newValue new value of resourceBundleName
     */
    public void setResourceBundleName(String newValue) {
        if(getResourceBundle()!=null) {
            trace("resourceBundle already set; ignoring %s", newValue);
            return;
        }
        try {
            if(classLoader==null) classLoader= ClassLoader.getSystemClassLoader();
            ResourceBundle rb = ResourceBundle.getBundle(newValue, Locale.getDefault(), classLoader);
            setResourceBundle(rb);
        } catch (MissingResourceException e) {
            trace("MissingResourceException: %s", e);
        }
        if(getResourceBundle()==null) try {
            classLoader= ClassLoader.getSystemClassLoader();
            ResourceBundle rb = ResourceBundle.getBundle(newValue, Locale.getDefault(), classLoader);
            setResourceBundle(rb);
        } catch (MissingResourceException e) {
            trace("MissingResourceException: %s", e);
        }
    }// </editor-fold>
    // <editor-fold defaultstate="collapsed" desc=" Property: name ">
    /**
     * Get the value of name
     *
     * @return the value of name
     */
    public String getName() {
        return (String)getValue(NAME);
    }

    /**
     * Set the value of name
     *
     * @param newValue new value of name
     */
    public void setName(String newValue) {
        putValue(NAME, newValue);
    }// </editor-fold>
    // <editor-fold defaultstate="collapsed" desc=" Property: code ">
    /**
     * Get the value of code
     *
     * @return the value of code
     */
    public String getCode() {
        return (String)getValue(CODE_KEY);
    }

    /**
     * Set the value of code
     *
     * @param newValue new value of code
     */
    public void setCode(String newValue) {
        putValue(CODE_KEY, newValue);
    }// </editor-fold>
    // <editor-fold defaultstate="collapsed" desc=" Property: selected ">
    /**
     * Get the value of selected
     *
     * @return the value of selected
     */
    public Boolean getSelected() {
        return (Boolean)getValue(SELECTED_KEY);
    }

    /**
     * Set the value of selected
     *
     * @param newValue new value of selected
     */
    public void setSelected(Boolean newValue) {
        putValue(SELECTED_KEY, newValue);
    }// </editor-fold>
    // <editor-fold defaultstate="collapsed" desc=" Property: propertiesFileName ">
    /**
     * Get the value of propertiesFileName
     *
     * @return the value of propertiesFileName
     */
    public String getPropertiesFileName() {
        return (String)getValue(PROP_KEY);
    }

    /**
     * Set the value of propertiesFileName
     *
     * @param stage new value of propertiesFileName
     */
    public void setPropertiesFileName(String newValue) {
        putValue(PROP_KEY, newValue);
    }// </editor-fold>



    @Override
    public Object getValue(String key) {
        final Object ret = super.getValue(key);
        return ret;
    }

    @Override
    public void putValue(String key, Object newValue) {
        trace("%s=%s", key, newValue);
        try {
            super.putValue(key, newValue);
            if (CODE_KEY.equals(key)) {
                init();
            } else if (PROP_KEY.equals(key)) {
                try {
                    setResourceBundle(new PropertyResourceBundle(new FileReader((String)newValue)));
                } catch (IOException ex) {
                    trace("I/O error: %s", getStackTrace(ex));
                }
            } else if (TEXT_KEY.equals(key)) {
                if (newValue != null) {
                    PublicMnemonicText.configure(this, (String) newValue);
                }
                init();
            } else if (FORM_KEY.equals(key)) {
                if (getResourceBundle() == null && newValue != null) {
                    Class cls = newValue.getClass();
                    classLoader = cls.getClassLoader();
                    if(classLoader==null) classLoader= ClassLoader.getSystemClassLoader();
                    try {
                        final String clbn = classBundleBaseName(cls);
                        internalSetResourceBundle(ResourceBundle.getBundle(clbn, Locale.getDefault(), classLoader));
                    } catch (MissingResourceException e) {
                        try {
                        internalSetResourceBundle(ResourceBundle.getBundle(cls.getName(), Locale.getDefault(), classLoader));
                        } catch (MissingResourceException e2) {
                            trace("Error: %s", getStackTrace(e2));
                        }
                    }
                }
                init();
            }
        } catch (Throwable e) {
            trace("Error: %s", getStackTrace(e));
        }
    }

    /**
     * Get a string from a resourcebundle, returning null if it does not exist.
     * @param key
     * @return
     */
    private String getStringNL(String key) {
        try {
            return getResourceBundle().getString(key);
        } catch (MissingResourceException e) {
            return null;
        }
    }
    /**
     * Loads the specified resource value as an icon.
     * @param key Property key value
     * @return Icon, or null if not specified or not found.
     */
    private Icon getIcon(String key) {
        String res = getStringNL(key);
        if(res == null) return null;
        if(res.startsWith("/")) res = res.substring(1);
        if(classLoader==null) classLoader = ClassLoader.getSystemClassLoader();
        URL url = classLoader.getResource(res);
        if(url == null) {
            trace("classloader could not find %s (key=%s)", res, key);
            return null;
        }
        return new ImageIcon(url);
    }
    /**
     * Loads the specified resource value as an integer.
     * @param key Property key value
     * @return Integer, or null if not specified or not found.
     */
    private Integer getInteger(String key) {
        String res = getStringNL(key);
        if(res == null) return null;
        return Integer.valueOf(res);
    }
    /**
     * Loads the specified resource value as a keystroke.
     * @param key Property key value
     * @return Keystroke, or null if not specified or not found.
     */
    private KeyStroke getKeyStroke(String key) {
        String res = getStringNL(key);
        if(res == null) return null;
        return KeyStroke.getKeyStroke(res);
    }
    /**
     * Loads the specified resource value as a key code.
     * @param key Property key value
     * @return Key code, or null if not specified or not found.
     */
    private Integer getKeyCode(String key) {
        KeyStroke ks = getKeyStroke(key);
        if(ks == null) return null;
        return ks.getKeyCode();
    }

    // endless loops are bad.
    private boolean inInit = false;
    /*
     * Init all of the javax.swing.Action properties for the @Action named
     * actionName.
     */
    private void init() {
        if(!inInit)
        try {
            inInit = true;

            String code = getCode();
            if (code == null) {
                trace("init: no code");
                return;
            }


            if (getForm() != null) {
                try {
                    // find the action method
                    actionMethod = getForm().getClass().getMethod(code);
                    // find the @Action annotation.
                    Action ac = actionMethod.getAnnotation(Action.class);
                    String eprop = ac.enabledProperty();
                    if(eprop!=null && !eprop.isEmpty()) {
                        Utils.addPropertyChangeListener(getForm(), eprop, enabledListener);
                    }
                    String sprop = ac.selectedProperty();
                    if(sprop!=null && !sprop.isEmpty()) {
                        Utils.addPropertyChangeListener(getForm(), sprop, selectedListener);
                    }
                } catch (NoSuchMethodException ex) {
                    log.severe(String.format("No method named %s in %s", code, getForm().getClass().getName()));
                } catch (SecurityException ex) {
                    throw new RuntimeException(ex);
                }
            }

            if (getResourceBundle() == null) {
                trace("init: no rb");
                return;
            }

            putValue(TEXT_KEY,getStringNL(code + ".Action.text"));
            putValue(MNEMONIC_KEY,getKeyCode(code + ".Action.mnemonic"));
            putValue(DISPLAYED_MNEMONIC_INDEX_KEY,getInteger(code + ".Action.displayedMnemonicIndex"));
            putValue(ACCELERATOR_KEY, getKeyStroke(code + ".Action.accelerator"));
            Icon icon = getIcon(code + ".Action.icon");
            if (icon != null) {
                putValue(SMALL_ICON,icon);
                putValue(LARGE_ICON_KEY,icon);
            }
            Icon smallIcon = getIcon(code + ".Action.smallIcon");
            if (smallIcon != null) {
                putValue(SMALL_ICON, smallIcon);
            }
            Icon largeIcon = getIcon(code + ".Action.largeIcon");
            if (largeIcon != null) {
                putValue(LARGE_ICON_KEY, largeIcon);
            }

            putValue(SHORT_DESCRIPTION,getStringNL(code + ".Action.shortDescription"));
            putValue(LONG_DESCRIPTION,getStringNL(code + ".Action.longDescription"));
            putValue(ACTION_COMMAND_KEY,getStringNL(code + ".Action.command"));

        } catch (RuntimeException ex) {
            trace("%s", getStackTrace(ex));
        } finally {
            inInit = false;
        }
    }


    /**
     * Invoke the action method.
     */
    @Override
    public void actionPerformed(ActionEvent e) {
        if(actionMethod == null) {
            log.severe("Action object has no method set.");
            return;
        }
        try {
            actionMethod.invoke(getForm());
        } catch (InvocationTargetException ex) {
            log.log(Level.SEVERE, "Error calling action", ex.getTargetException());
            throw new RuntimeException(ex.getTargetException());
        } catch (Exception ex) {
            log.log(Level.SEVERE, "Error calling action", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * Convert the stack trace to a string.
     * @param ex Throwable
     * @return String representation
     */
    private String getStackTrace(Throwable ex) {
        StringWriter sw = new StringWriter();
        ex.printStackTrace(new PrintWriter(sw));
        return sw.toString();
    }


    /**
     * Convert a class name to an eponymous resource bundle in the
     * resources subpackage.  For example, given a class named
     * com.foo.bar.MyClass, the ResourceBundle name would be
     * "com.foo.bar.resources.MyClass"  If MyClass is an inner class,
     * only its "simple name" is used.  For example, given an
     * inner class named com.foo.bar.OuterClass$InnerClass, the
     * ResourceBundle name would be "com.foo.bar.resources.InnerClass".
     * Although this could result in a collision, creating more
     * complex rules for inner classes would be a burden for
     * developers.
     */
    public static String classBundleBaseName(Class cls) {
        String className = cls.getName();
        StringBuilder sb = new StringBuilder();
        int i = className.lastIndexOf('.');
        if (i > 0) {
            sb.append(className.substring(0, i));
            sb.append(".resources."); //NOI18N
            sb.append(cls.getSimpleName());
        } else {
            sb.append("resources."); //NOI18N
            sb.append(cls.getSimpleName());
        }
        return sb.toString();
    }
}
TOP

Related Classes of net.megabyte.nosaf.MBAction

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.