Clover coverage report -
Coverage timestamp: Sun Apr 18 2004 21:32:30 EDT
file stats: LOC: 473   Methods: 21
NCLOC: 196   Classes: 3
 
 Source file Conditionals Statements Methods TOTAL
DynamicResource.java 52.6% 68% 76.2% 65.4%
coverage coverage
 1   
 /*
 2   
  * SimplyHTML, a word processor based on Java, HTML and CSS
 3   
  * Copyright (C) 2002 Ulrich Hilger
 4   
  *
 5   
  * This program is free software; you can redistribute it and/or
 6   
  * modify it under the terms of the GNU General Public License
 7   
  * as published by the Free Software Foundation; either version 2
 8   
  * of the License, or (at your option) any later version.
 9   
  *
 10   
  * This program is distributed in the hope that it will be useful,
 11   
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12   
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13   
  * GNU General Public License for more details.
 14   
  *
 15   
  * You should have received a copy of the GNU General Public License
 16   
  * along with this program; if not, write to the Free Software
 17   
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 18   
  */
 19   
 
 20   
 import java.awt.Component;
 21   
 import java.util.ResourceBundle;
 22   
 import java.util.MissingResourceException;
 23   
 import java.util.Locale;
 24   
 import java.util.Hashtable;
 25   
 import java.util.Enumeration;
 26   
 import javax.swing.event.*;
 27   
 import javax.swing.*;
 28   
 import java.beans.PropertyChangeListener;
 29   
 import java.beans.PropertyChangeEvent;
 30   
 import java.net.URL;
 31   
 
 32   
 /**
 33   
  * Provides methods to dynamically combine components and resource bundles.
 34   
  *
 35   
  * <p>The actions, menus and menuitems created by this object are
 36   
  * stored privately inside this object. This allows for later
 37   
  * access to one of the stored items through the action command name.</p>
 38   
  *
 39   
  * <p><b>IMPORTANT:</b> Action command names must be unique, if actions
 40   
  * or menus are added to an instance of this class and if the actions or
 41   
  * menus are defined in different ResourceBundles, the action names
 42   
  * must be unique over all ResourceBundles involved, because the action
 43   
  * names are used as identifiers for connection of actions to compnents
 44   
  * such as menus and menu items.</p>
 45   
  *
 46   
  * <p>Component creation methods such as createMenu or createMenuItem
 47   
  * expect definitions coming from a ResourceBundle, typically a
 48   
  * text file ending with '.properties'.</p>
 49   
  *
 50   
  * <p>
 51   
  * Inside the .properties file, a menu definition is looking similar to
 52   
  * <pre>
 53   
  *   # plugin menu definition
 54   
  *   plugin=test1 test2 test3
 55   
  *   pluginLabel=Test Plug-In
 56   
  *
 57   
  *   # plugin menu items
 58   
  *   test1Label=Test 1
 59   
  *   test1Image=images/test1.gif
 60   
  *   test1Tip=test menu item 1
 61   
  *   test2Label=Test 2
 62   
  *   test3Label=Test 3
 63   
  * </pre>
 64   
  * </p>
 65   
  *
 66   
  * <p>
 67   
  * The calling class has to define actions named accordingly, e.g.
 68   
  * <pre>
 69   
  *    DynamicResource dynRes = new DynamicResource("com.foo.bar.myPlugin");
 70   
  *    dynRes.addAction("test1", new MyAction("test1");
 71   
  *    dynRes.addAction("test2", new MyAction("test2");
 72   
  *    dynRes.addAction("test3", new MyAction("test3");
 73   
  * </pre>
 74   
  * </p>
 75   
  *
 76   
  * @author Ulrich Hilger
 77   
  * @author Light Development
 78   
  * @author <a href="http://www.lightdev.com">http://www.lightdev.com</a>
 79   
  * @author <a href="mailto:info@lightdev.com">info@lightdev.com</a>
 80   
  * @author published under the terms and conditions of the
 81   
  *      GNU General Public License,
 82   
  *      for details see file gpl.txt in the distribution
 83   
  *      package of this software
 84   
  *
 85   
  * @version stage 11, April 27, 2003
 86   
  */
 87   
 
 88   
 public class DynamicResource {
 89   
 
 90   
   /** name constant for labels in the resource file */
 91   
   private static final String labelSuffix = "Label";
 92   
 
 93   
   /** name constant for action commands in the resource file */
 94   
   private static final String actionSuffix = "Action";
 95   
 
 96   
   /** name constant for indicating image resources in the resource file */
 97   
   public static final String imageSuffix = "Image";
 98   
 
 99   
   /** name constant for tool tip strings in the resource file */
 100   
   public static final String toolTipSuffix = "Tip";
 101   
 
 102   
   /** name constant for selected icon names in the resource file */
 103   
   public static final String selectedIconSuffix = "SelectedIcon";
 104   
 
 105   
   /** indicator for menu separators */
 106   
   public static final String menuSeparatorKey = "-";
 107   
 
 108   
   /** dynamic storage for menu items */
 109   
   private Hashtable menuItems = new Hashtable();
 110   
 
 111   
   /** dynamic storage for actions */
 112   
   private Hashtable commands = new Hashtable();
 113   
 
 114   
   /** dynamic storage for menus */
 115   
   private Hashtable menus = new Hashtable();
 116   
 
 117   
   public static final String IMAGE_EMPTY = "empty.gif";
 118   
 
 119   
   /**
 120   
    * construct a new DynamicResource.
 121   
    */
 122  11
   public DynamicResource() {
 123   
   }
 124   
 
 125   
   /**
 126   
    * add an action to this <code>DynamicResource</code>
 127   
    *
 128   
    * @param cmd  the internal identifier for the action
 129   
    * @param action  the action to associate with actionCommand
 130   
    */
 131  4800
   public void addAction(String cmd, Action action) {
 132  4800
     commands.put(cmd, action);
 133   
   }
 134   
 
 135   
   /**
 136   
    * Create a menu bar.  This reads the
 137   
    * definition of the menu from the associated resource file.
 138   
    *
 139   
    * @param resources  the ResourceBundle to get the menu definition from
 140   
    * @param name  name of the menu bar definition
 141   
    *
 142   
    * @return the created menu bar
 143   
    */
 144  75
   public JMenuBar createMenubar(ResourceBundle resources, String name) {
 145  75
     JMenuItem mi;
 146  75
     JMenuBar mb = new JMenuBar();
 147  75
     String[] menuKeys = Util.tokenize(getResourceString(resources, name), " ");
 148  75
     for (int i = 0; i < menuKeys.length; i++) {
 149  600
       JMenu m = createMenu(resources, menuKeys[i]);
 150  600
       if (m != null) {
 151  600
         mb.add(m);
 152   
       }
 153   
     }
 154  75
     return mb;
 155   
   }
 156   
 
 157   
   /**
 158   
    * Create a menu for the app.  This reads the
 159   
    * definition of the menu from the associated resource file.
 160   
    *
 161   
    * @param resources  the ResourceBundle to get the menu definition from
 162   
    * @param key  the key of the menu definition in the resource file
 163   
    * @return the created menu
 164   
    */
 165  600
   public JMenu createMenu(ResourceBundle resources, String key) {
 166  600
     JMenu menu = null;
 167  600
     String def = getResourceString(resources, key);
 168  600
     if(def == null) {
 169  0
       def = "";
 170   
     }
 171  600
     String[] itemKeys = Util.tokenize(def, " ");
 172  600
     menu = new JMenu(getResourceString(resources, key + labelSuffix));
 173  600
     for (int i = 0; i < itemKeys.length; i++) {
 174  6075
       if (itemKeys[i].equals(menuSeparatorKey)) {
 175  2025
         menu.addSeparator();
 176   
       }
 177   
       else {
 178  4050
         JMenuItem mi = createMenuItem(resources, itemKeys[i]);
 179  4050
         menu.add(mi);
 180   
       }
 181   
     }
 182  600
     menu.addMenuListener(new DynamicMenuListener());
 183   
     /**
 184   
      * store the menu in the menus hashtable for possible later use
 185   
      */
 186  600
     menus.put(key, menu);
 187  600
     return menu;
 188   
   }
 189   
 
 190  75
   public JMenu getMenu(String cmd) {
 191  75
     return (JMenu) menus.get(cmd);
 192   
   }
 193   
 
 194   
   /**
 195   
    * create a menu item
 196   
    *
 197   
    * @param resources  the ResourceBundle to get the item definition from
 198   
    * @param cmd the action command to be associated
 199   
    *      with the new menu item
 200   
    * @return the created menu item
 201   
    */
 202  4050
   public JMenuItem createMenuItem(ResourceBundle resources, String cmd) {
 203   
     /**
 204   
      * create a new menu item with the appropriate label from the
 205   
      * resource file. This label later is set from the action this
 206   
      * menu item is associated to (see below).
 207   
      */
 208  4050
     JMenuItem mi;
 209  4050
     mi = new JMenuItem(getResourceString(resources, cmd + labelSuffix));
 210   
 
 211  4050
     String astr = getResourceString(resources, cmd + actionSuffix);
 212  4050
     if (astr == null) {
 213  4050
       astr = cmd;
 214   
     }
 215   
     //System.out.println(astr);
 216  4050
     mi.setActionCommand(astr);
 217   
 
 218   
     /**
 219   
      * connect action and menu item with appropriate listeners
 220   
      */
 221  4050
     Action a = getAction(astr);
 222  4050
     if (a != null) {
 223   
         //System.out.println(cmd + "  not null");
 224  4050
       Object aKey = a.getValue(AbstractAction.ACCELERATOR_KEY);
 225  4050
       if(aKey != null) {
 226  1050
         mi.setAccelerator((KeyStroke) aKey);
 227   
       }
 228   
       /**
 229   
        * add Action 'a' as the listener to action events
 230   
        * fired from this menu, i.e. execute action 'a' with
 231   
        * menu item 'mi'
 232   
        */
 233  4050
       mi.addActionListener(a);
 234   
 
 235   
       /**
 236   
        * cause an instance of inner class ActionChangeListener
 237   
        * to listen to property changes of Action 'a' and to apply
 238   
        * changed properties of Action 'a' to menu item 'mi'
 239   
        */
 240  4050
       a.addPropertyChangeListener(createActionChangeListener(mi));
 241   
 
 242   
       /**
 243   
        * if the action has an image,
 244   
        * associate it with the menu item
 245   
        */
 246  4050
       Icon icon = (Icon) a.getValue(Action.SMALL_ICON);
 247  4050
       if (icon != null) {
 248  2175
         mi.setHorizontalTextPosition(JButton.RIGHT);
 249  2175
         mi.setIcon(icon);
 250   
       }
 251   
 
 252   
       /**
 253   
        * initially set the enabled state of the menu item
 254   
        * according to its action's enabled state
 255   
        */
 256  4050
       mi.setEnabled(a.isEnabled());
 257   
     }
 258   
     else {
 259  0
       mi.setEnabled(false);
 260   
     }
 261   
 
 262   
     /**
 263   
      * store the menu item in the menuItems hashtable for possible later use
 264   
      */
 265  4050
     menuItems.put(cmd, mi);
 266   
 
 267  4050
     return mi;
 268   
   }
 269   
 
 270   
   /**
 271   
    * get a string from the resources file
 272   
    *
 273   
    * @param resources  the ResourceBundle to get the string from
 274   
    * @param nm  the key of the string
 275   
    * @return the string for the given key or null if not found
 276   
    */
 277  21143
   public String getResourceString(ResourceBundle resources, String nm) {
 278  21143
     String str = null;
 279  21143
     try {
 280   
       //System.out.println("getResourceString nm=" + nm);
 281  21143
       str = resources.getString(nm);
 282   
     }
 283   
     catch (MissingResourceException mre) { }
 284  21143
     return str;
 285   
   }
 286   
 
 287   
   /**
 288   
    * listen to menu select events for proper updating of menu items
 289   
    *
 290   
    * whenever a menu is selected, its menu items are iterated and the
 291   
    * update method of the item's action is called causing
 292   
    * the menu item to reflect the correct enabled state.
 293   
    *
 294   
    * As each menu item is connected with a PropertyChangeListener
 295   
    * listening to property changes on it'Saction, the menu item is
 296   
    * updated by the PropertyChangeListener whenever the enabledState
 297   
    * of the action changes.
 298   
    */
 299   
   private class DynamicMenuListener implements MenuListener {
 300  600
     public DynamicMenuListener() {
 301   
     }
 302  0
     public void menuSelected(MenuEvent e) {
 303  0
       Component[] items = ((JMenu) e.getSource()).getMenuComponents();
 304  0
       Action action;
 305  0
       for(int i = 0; i < items.length; i++) {
 306  0
         if(items[i] instanceof JPopupMenu.Separator) {
 307   
         }
 308  0
         else if(items[i] instanceof JMenuItem) {
 309  0
           action = getAction(((JMenuItem) items[i]).getActionCommand());
 310  0
           if(action instanceof SHTMLAction) {
 311  0
             ((SHTMLAction) action).update();
 312   
           }
 313   
         } 
 314   
       }
 315   
     }
 316  0
     public void menuDeselected(MenuEvent e) {
 317   
     }
 318  0
     public void menuCanceled(MenuEvent e) {
 319   
     }
 320   
   }
 321   
 
 322   
   /**
 323   
    * get an action from the commands table
 324   
    *
 325   
    * @param cmd  the name of the action the get
 326   
    * @return the action found for the given name
 327   
    */
 328  5929
   public Action getAction(String cmd) {
 329  5929
     return (Action) commands.get(cmd);
 330   
   }
 331   
 
 332   
   /**
 333   
    * get all actions registered in this <code>DynamicResource</code>
 334   
    *
 335   
    * @return all actions
 336   
    */
 337  231
   public Enumeration getActions() {
 338  231
     return commands.elements();
 339   
   }
 340   
 
 341   
   /** create our PropertyChangeListener implementation */
 342  4050
   private PropertyChangeListener createActionChangeListener(JMenuItem b) {
 343  4050
     return new ActionChangedListener(b);
 344   
   }
 345   
 
 346   
   /**
 347   
    * associate a menu item to an action.
 348   
    *
 349   
    * When registering this
 350   
    * action listener with an action, it gets informed by
 351   
    * property changes of that particular action.
 352   
    *
 353   
    * By passing a menu item to the constructor of ActionChangedListener,
 354   
    * an instance of ActionChangedListener 'remembers' the menu item
 355   
    * its property are associated to.
 356   
    */
 357   
   private class ActionChangedListener implements PropertyChangeListener {
 358   
     JMenuItem menuItem;
 359   
 
 360  4050
     ActionChangedListener(JMenuItem mi) {
 361  4050
       super();
 362  4050
       this.menuItem = mi;
 363   
     }
 364   
 
 365  4688
     public void propertyChange(PropertyChangeEvent e) {
 366  4688
       String propertyName = e.getPropertyName();
 367  4688
       if (e.getPropertyName().equals(Action.NAME)) {
 368  0
         String text = (String) e.getNewValue();
 369  0
         menuItem.setText(text);
 370   
       }
 371  4688
       else if (propertyName.equals("enabled")) {
 372  4688
         Boolean enabledState = (Boolean) e.getNewValue();
 373  4688
         menuItem.setEnabled(enabledState.booleanValue());
 374   
       }
 375   
     }
 376   
   }
 377   
 
 378   
   /**
 379   
    * get the menu item that was created for the given
 380   
    * command.
 381   
    *
 382   
    * @param cmd  name of the action.
 383   
    * @return item created for the given command or null
 384   
    *  if one wasn't created.
 385   
    */
 386  0
   public JMenuItem getMenuItem(String cmd) {
 387  0
     return (JMenuItem) menuItems.get(cmd);
 388   
   }
 389   
 
 390   
   /**
 391   
    * get the icon for a given command.
 392   
    *
 393   
    * <p>If the resource bundle has a reference to an icon for the
 394   
    * given commamd, an icon is created for the respective image resource.
 395   
    * otherwise, null is returned.</p>
 396   
    *
 397   
    * @param resources  the ResourceBundle to get the icon from
 398   
    * @param cmd  the command an icon is requested for
 399   
    *
 400   
    * @return the icon for that command or null, if none is present
 401   
    *        for this command
 402   
    */
 403  4876
   public Icon getIconForCommand(ResourceBundle resources, String cmd) {
 404  4876
     return getIconForName(resources, cmd + imageSuffix);
 405   
   }
 406   
 
 407  5326
   public Icon getIconForName(ResourceBundle resources, String name) {
 408  5326
     Icon icon = null;
 409  5326
     URL url = getResource(resources, name);
 410   
     //System.out.println("getIconForName name=" + name + ", url=" + url);
 411  5326
     if (url != null) {
 412  2925
       icon = new ImageIcon(url);
 413   
     }
 414  5326
     return icon;
 415   
   }
 416   
 
 417   
   /**
 418   
    * get the location of a resource.
 419   
    *
 420   
    * <p>Resources such as images are delivered with the application in
 421   
    * the path containing the application's classes. The resources file
 422   
    * coming with SimplyHTML has a key for every resource pointing to
 423   
    * the subdirectory relative to the class path.</p>
 424   
    *
 425   
    * @param resources  the ResourceBundle to get the resource from
 426   
    * @param key  the key of the resource in the resource file
 427   
    * @return the resource location as a URL
 428   
    */
 429  5401
   public URL getResource(ResourceBundle resources, String key) {
 430  5401
     String name = getResourceString(resources, key);
 431  5401
     if (name != null/* && !name.endsWith(IMAGE_EMPTY)*/) {
 432  3000
       URL url = this.getClass().getResource(name);
 433  3000
       return url;
 434   
     }
 435  2401
     return null;
 436   
   }
 437   
 
 438   
   /**
 439   
    * Create a tool bar.  This reads the definition of a tool bar
 440   
    * from the associated resource file.
 441   
    *
 442   
    * @param resources  the ResourceBundle to get the tool bar definition from
 443   
    * @param nm  the name of the tool bar definition in the resource file
 444   
    *
 445   
    * @return the created tool bar
 446   
    */
 447  0
   public JToolBar createToolBar(ResourceBundle resources, String nm) {
 448  0
     Action action;
 449  0
     AbstractButton newButton;
 450  0
     java.awt.Dimension buttonSize = new java.awt.Dimension(24,24);
 451  0
     java.awt.Dimension separatorSize = new java.awt.Dimension(3, 20);
 452  0
     JSeparator separator;
 453  0
     String[] itemKeys = Util.tokenize(getResourceString(resources, nm), " ");
 454  0
     JToolBar toolBar = new JToolBar();
 455  0
     toolBar.putClientProperty("JToolBar.isRollover", Boolean.TRUE );
 456  0
     for (int i = 0; i < itemKeys.length; i++) {
 457   
       /** special handling for separators */
 458  0
       if (itemKeys[i].equals(menuSeparatorKey)) {
 459  0
         separator = new JSeparator(JSeparator.VERTICAL);
 460  0
         toolBar.add(separator);
 461   
       }
 462   
       else {
 463  0
         action = getAction(itemKeys[i]);
 464  0
         newButton = toolBar.add(action);
 465  0
         newButton.setMinimumSize(buttonSize);
 466  0
         newButton.setPreferredSize(buttonSize);
 467  0
         newButton.setMaximumSize(buttonSize);
 468  0
         newButton.setFocusPainted(false);
 469   
       }
 470   
     }
 471  0
     return toolBar;
 472   
   }
 473   
 }