Coverage Report - org.geotoolkit.gui.swing.image.RegisteredOperationBrowser
 
Classes in this File Line Coverage Branch Coverage Complexity
RegisteredOperationBrowser
58 %
58/99
47 %
20/42
3,667
RegisteredOperationBrowser$1
33 %
1/3
N/A
3,667
RegisteredOperationBrowser$2
100 %
2/2
N/A
3,667
RegisteredOperationBrowser$CellRenderer
100 %
21/21
50 %
7/14
3,667
 
 1  
 /*
 2  
  *    Geotoolkit.org - An Open Source Java GIS Toolkit
 3  
  *    http://www.geotoolkit.org
 4  
  *
 5  
  *    (C) 2003-2012, Open Source Geospatial Foundation (OSGeo)
 6  
  *    (C) 2009-2012, Geomatys
 7  
  *
 8  
  *    This library is free software; you can redistribute it and/or
 9  
  *    modify it under the terms of the GNU Lesser General Public
 10  
  *    License as published by the Free Software Foundation;
 11  
  *    version 2.1 of the License.
 12  
  *
 13  
  *    This library is distributed in the hope that it will be useful,
 14  
  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 15  
  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 16  
  *    Lesser General Public License for more details.
 17  
  */
 18  
 package org.geotoolkit.gui.swing.image;
 19  
 
 20  
 import java.util.List;
 21  
 import java.util.Arrays;
 22  
 import java.util.Locale;
 23  
 import java.util.Comparator;
 24  
 import java.util.Collections;
 25  
 import java.util.ResourceBundle;
 26  
 import java.util.MissingResourceException;
 27  
 import java.util.logging.Level;
 28  
 
 29  
 import java.awt.Component;
 30  
 import java.awt.BorderLayout;
 31  
 import javax.swing.Icon;
 32  
 import javax.swing.Box;
 33  
 import javax.swing.JTree;
 34  
 import javax.swing.JLabel;
 35  
 import javax.swing.JComponent;
 36  
 import javax.swing.JScrollPane;
 37  
 import javax.swing.BorderFactory;
 38  
 import javax.swing.tree.TreePath;
 39  
 import javax.swing.tree.TreeModel;
 40  
 import javax.swing.tree.TreeSelectionModel;
 41  
 import javax.swing.tree.DefaultTreeCellRenderer;
 42  
 import javax.swing.event.TreeSelectionEvent;
 43  
 import javax.swing.event.TreeSelectionListener;
 44  
 import java.awt.image.renderable.ParameterBlock; // For javadoc
 45  
 
 46  
 import javax.media.jai.JAI;
 47  
 import javax.media.jai.OperationRegistry;
 48  
 import javax.media.jai.OperationDescriptor;
 49  
 import javax.media.jai.ParameterListDescriptor;
 50  
 import javax.media.jai.RegistryElementDescriptor;
 51  
 
 52  
 import org.geotoolkit.util.converter.Classes;
 53  
 import org.geotoolkit.gui.swing.IconFactory;
 54  
 import org.geotoolkit.gui.swing.tree.TreeNode;
 55  
 import org.geotoolkit.gui.swing.tree.NamedTreeNode;
 56  
 import org.geotoolkit.gui.swing.tree.DefaultTreeModel;
 57  
 import org.geotoolkit.gui.swing.tree.DefaultMutableTreeNode;
 58  
 import org.geotoolkit.resources.Vocabulary;
 59  
 import org.geotoolkit.util.logging.Logging;
 60  
 
 61  
 
 62  
 /**
 63  
  * Browse through the registered JAI operations. This widget display a tree build from a
 64  
  * JAI's {@link OperationRegistry}. The tree has the following hierarchy:
 65  
  * <p>
 66  
  * <ul>
 67  
  *   <li>At the first level, all {@linkplain OperationRegistry#getRegistryModes() registry modes}
 68  
  *       (e.g. "rendered", "renderable", etc.) in alphabetical order.</li>
 69  
  *   <li>At the second level, all {@linkplain OperationRegistry#getDescriptors(String) operation
 70  
  *       descriptors} (e.g. "Affine", "Multiply", etc.) registered in each
 71  
  *       registry mode, in alphabetical order. This is the operation name to be given to
 72  
  *       {@link JAI#create(String,ParameterBlock) JAI.create(...)} methods.</li>
 73  
  *   <li>At the third level, a list of
 74  
  *       {@linkplain RegistryElementDescriptor#getParameterListDescriptor(String) parameters}
 75  
  *       as leafs, and the list of
 76  
  *       {@linkplain OperationRegistry#getOrderedProductList implementing products} as nodes.
 77  
  *       This level is not sorted in alphabetical order, since the ordering is relevant.</li>
 78  
  *   <li>At the last level, a list of {@linkplain OperationRegistry#getOrderedFactoryList
 79  
  *       factories} as leafs. This level is not sorted in alphabetical order, since the ordering
 80  
  *       is relevant.</li>
 81  
  * </ul>
 82  
  *
 83  
  * <table cellspacing="24" cellpadding="12" align="center"><tr valign="top">
 84  
  * <td width="500" bgcolor="lightblue">
 85  
  * {@section Demo}
 86  
  * To try this component in your browser, see the
 87  
  * <a href="http://www.geotoolkit.org/demos/geotk-simples/applet/RegisteredOperationBrowser.html">demonstration applet</a>.
 88  
  * </td></tr></table>
 89  
  *
 90  
  * @author Martin Desruisseaux (IRD)
 91  
  * @version 3.12
 92  
  *
 93  
  * @since 2.3
 94  
  * @module
 95  
  */
 96  
 @SuppressWarnings("serial")
 97  0
 public class RegisteredOperationBrowser extends JComponent {
 98  
     /**
 99  
      * The text area for operation's description.
 100  
      */
 101  1
     private final JLabel description = new JLabel(" ");
 102  
 
 103  
     /**
 104  
      * The text area for the version and vendor.
 105  
      */
 106  1
     private final JLabel version = new JLabel(" ");
 107  
 
 108  
     /**
 109  
      * Constructs a new operation browser for the default {@link JAI} instance.
 110  
      */
 111  
     public RegisteredOperationBrowser() {
 112  1
         this(getTree());
 113  1
     }
 114  
 
 115  
     /**
 116  
      * Constructs a new operation browser for the specified operation registry.
 117  
      *
 118  
      * @param registry The operation registry to use for fetching operations.
 119  
      */
 120  
     public RegisteredOperationBrowser(final OperationRegistry registry) {
 121  0
         this(getTree(registry, getDefaultLocale()));
 122  0
     }
 123  
 
 124  
     /**
 125  
      * Constructs a new operation browser for operations from the specified tree.
 126  
      *
 127  
      * @param model The tree model built by one of {@link #getTree} methods.
 128  
      */
 129  1
     private RegisteredOperationBrowser(final TreeModel model) {
 130  1
         setLayout(new BorderLayout());
 131  1
         final JTree tree = new JTree(model);
 132  1
         tree.setBorder(BorderFactory.createEmptyBorder(6, 6, 0, 0));
 133  1
         add(new JScrollPane(tree), BorderLayout.CENTER);
 134  
         /*
 135  
          * Add labels (description and version number).
 136  
          */
 137  1
         final Box labels = Box.createVerticalBox();
 138  1
         labels.add(description);
 139  1
         labels.add(version);
 140  1
         labels.setBorder(BorderFactory.createEmptyBorder(3, 6, 3, 0));
 141  1
         add(labels, BorderLayout.SOUTH);
 142  
         /*
 143  
          * Configure the operations tree.
 144  
          */
 145  1
         tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
 146  1
         tree.addTreeSelectionListener(new TreeSelectionListener() {
 147  
             @Override public void valueChanged(final TreeSelectionEvent event) {
 148  0
                 selected(event.getNewLeadSelectionPath());
 149  0
             }
 150  
         });
 151  
         /*
 152  
          * Set icons (optional)
 153  
          */
 154  1
         tree.setCellRenderer(new CellRenderer());
 155  1
     }
 156  
 
 157  
     /**
 158  
      * Invoked when the user selected a new operation in the tree. This method find the
 159  
      * {@link OperationDescriptor} for the selected node and invokes {@link #selected}.
 160  
      *
 161  
      * @param path The selected tree path, or {@code null} if none.
 162  
      */
 163  
     private void selected(final TreePath path) {
 164  0
         if (path != null) {
 165  0
             for (int i=path.getPathCount(); --i>=0;) {
 166  0
                 final Object component = path.getPathComponent(i);
 167  0
                 Object candidate = component;
 168  0
                 if (candidate instanceof TreeNode) {
 169  0
                     candidate = ((TreeNode) candidate).getUserObject();
 170  
                     /*
 171  
                      * Note: The missing 'getUserObject()' method is fixed
 172  
                      *       in Geotk TreeNode, not the Swing one...
 173  
                      */
 174  
                 }
 175  0
                 if (candidate instanceof OperationDescriptor) {
 176  0
                     int index = -1;
 177  
                     /*
 178  
                      * Fetch the parameter index. Note: the Swing TreeNode is sufficient
 179  
                      * for this task (no need for the fixed Geotk's TreeNode).
 180  
                      */
 181  0
                     if (component instanceof javax.swing.tree.TreeNode) {
 182  0
                         final javax.swing.tree.TreeNode node = (javax.swing.tree.TreeNode)component;
 183  0
                         final Object leaf = path.getLastPathComponent();
 184  0
                         for (index=node.getChildCount(); --index>=0;) {
 185  0
                             final javax.swing.tree.TreeNode param = node.getChildAt(index);
 186  0
                             if (param==leaf && !param.getAllowsChildren()) {
 187  0
                                 break;
 188  
                             }
 189  0
                         }
 190  
                     }
 191  0
                     selected((OperationDescriptor) candidate, index);
 192  0
                     return;
 193  
                 }
 194  0
             }
 195  
         }
 196  0
         selected(null, -1);
 197  0
     }
 198  
 
 199  
     /**
 200  
      * Invoked when the user selected a new operation in the tree. The default implementation
 201  
      * display the operation or parameter description in the text area.
 202  
      *
 203  
      * @param operation The selected operation, or {@code null} if no operation is
 204  
      *        selected.
 205  
      * @param param Index of the selected parameter, or {@code -1} if no parameter
 206  
      *        is selected.
 207  
      */
 208  
     protected void selected(final OperationDescriptor operation, final int param) {
 209  0
         String description = " ";
 210  0
         String version     = " ";
 211  0
         if (operation != null) {
 212  
             final String key;
 213  0
             final Locale locale = getLocale();
 214  0
             final ResourceBundle resources = operation.getResourceBundle(locale);
 215  0
             if (param >= 0) {
 216  0
                 key = "arg" + param + "Desc";
 217  
             } else {
 218  0
                 key = "Description";
 219  
             }
 220  
             try {
 221  0
                 description = resources.getString(key);
 222  0
                 version = Vocabulary.getResources(locale).getString(Vocabulary.Keys.VERSION_$1,
 223  
                         resources.getString("Version")) + ", " + resources.getString("Vendor");
 224  0
             } catch (MissingResourceException exception) {
 225  
                 /*
 226  
                  * A description was missing for this operation or parameter. This is not a big
 227  
                  * deal; just left some label blank. Log the exception with a low level, since
 228  
                  * this warning is not really important.
 229  
                  */
 230  0
                 Logging.getLogger(RegisteredOperationBrowser.class).log(Level.FINER,
 231  
                         exception.getLocalizedMessage(), exception);
 232  0
             }
 233  
         }
 234  0
         this.description.setText(description);
 235  0
         this.version    .setText(version    );
 236  0
     }
 237  
 
 238  
     /**
 239  
      * Returns a tree view of all operations registered in the default {@link JAI} instance.
 240  
      * Labels will be formatted in the Swing {@linkplain #getDefaultLocale default locale}.
 241  
      *
 242  
      * @return All JAI operations as a tree.
 243  
      */
 244  
     public static TreeModel getTree() {
 245  1
         return getTree(JAI.getDefaultInstance().getOperationRegistry(), getDefaultLocale());
 246  
     }
 247  
 
 248  
     /**
 249  
      * Returns a tree view of all operations registered in the given registry.
 250  
      *
 251  
      * @param  registry The registry (e.g. {@link JAI#getOperationRegistry()}).
 252  
      * @param  locale The locale (e.g. {@link Locale#getDefault()}).
 253  
      * @return All JAI operations as a tree.
 254  
      *
 255  
      * @see #getTree()
 256  
      * @see JAI#getDefaultInstance()
 257  
      * @see JAI#getOperationRegistry()
 258  
      * @see Locale#getDefault()
 259  
      */
 260  
     public static TreeModel getTree(final OperationRegistry registry, final Locale locale) {
 261  1
         final Vocabulary resources = Vocabulary.getResources(locale);
 262  1
         final DefaultMutableTreeNode root = new DefaultMutableTreeNode(
 263  
                 resources.getString(Vocabulary.Keys.OPERATIONS));
 264  
         /*
 265  
          * Add registry modes ("rendered", "renderable", etc.),
 266  
          * and gets the operation descriptors for each mode.
 267  
          */
 268  1
         final String[] modes = registry.getRegistryModes();
 269  1
         Arrays.sort(modes);
 270  8
         for (int i=0; i<modes.length; i++) {
 271  7
             final String mode = modes[i];
 272  7
             final DefaultMutableTreeNode modeNode = new DefaultMutableTreeNode(mode);
 273  
             @SuppressWarnings("unchecked")
 274  7
             final List<RegistryElementDescriptor> descriptors = registry.getDescriptors(mode);
 275  950
             Collections.sort(descriptors, new Comparator<RegistryElementDescriptor>() {
 276  
                 @Override public int compare(final RegistryElementDescriptor desc1,
 277  
                                              final RegistryElementDescriptor desc2)
 278  
                 {
 279  943
                     return desc1.getName().compareTo(desc2.getName());
 280  
                 }
 281  
             });
 282  
             /*
 283  
              * Add the operations ("add", "convolve", etc.) and their parameters.
 284  
              */
 285  7
             for (final RegistryElementDescriptor descriptor : descriptors) {
 286  
                 final DefaultMutableTreeNode descriptorNode;
 287  
                 final ParameterListDescriptor param;
 288  179
                 descriptorNode = new NamedTreeNode(getName(descriptor, locale), descriptor);
 289  179
                 param          = descriptor.getParameterListDescriptor(mode);
 290  179
                 if (param != null) {
 291  177
                     final String[] names = param.getParamNames();
 292  177
                     if (names != null) {
 293  
                         // No sorting; the order is relevant
 294  483
                         for (int j=0; j<names.length; j++) {
 295  
                             // Should not be NamedTreeNode, because the later is used for
 296  
                             // differentiating parameters and implementations (see below).
 297  364
                             descriptorNode.add(new DefaultMutableTreeNode(names[j], false));
 298  
                         }
 299  
                     }
 300  
                 }
 301  
                 /*
 302  
                  * Add the implementing products and the factories, if any.
 303  
                  */
 304  179
                 final String operationName = descriptor.getName();
 305  
                 @SuppressWarnings("unchecked")
 306  179
                 final List<String> products = registry.getOrderedProductList(mode, operationName);
 307  179
                 if (products != null) {
 308  
                     final DefaultMutableTreeNode productsNode;
 309  113
                     productsNode = new DefaultMutableTreeNode(
 310  
                             resources.getString(Vocabulary.Keys.IMPLEMENTATIONS));
 311  113
                     for (final String product : products) {
 312  
                         final DefaultMutableTreeNode productNode;
 313  113
                         productNode = new DefaultMutableTreeNode(product);
 314  113
                         final List<?> factories = registry.getOrderedFactoryList(mode, operationName, product);
 315  113
                         if (factories != null) {
 316  113
                             for (final Object factory : factories) {
 317  171
                                 productNode.add(new NamedTreeNode(
 318  
                                         Classes.getShortClassName(factory), factory, false));
 319  
                                 // The node class (NamedTreeNode) should be different from the
 320  
                                 // node for parameters (see above), in order to differentiate
 321  
                                 // those leafs in the cell renderer.
 322  
                             }
 323  
                         }
 324  113
                         productsNode.add(productNode);
 325  113
                     }
 326  113
                     descriptorNode.add(productsNode);
 327  
                 }
 328  179
                 modeNode.add(descriptorNode);
 329  179
             }
 330  7
             root.add(modeNode);
 331  
         }
 332  1
         return new DefaultTreeModel(root, true);
 333  
     }
 334  
 
 335  
     /**
 336  
      * Returns the localized name for the given descriptor. The name will be fecth from the
 337  
      * {@code "LocalName"} {@linkplain OperationDescriptor#getResourceBundle resource bundle},
 338  
      * if available. Otherwise, the {@linkplain RegistryElementDescriptor#getName non-localized
 339  
      * name} is returned.
 340  
      */
 341  
     private static String getName(final RegistryElementDescriptor descriptor, final Locale locale) {
 342  179
         if (descriptor instanceof OperationDescriptor) {
 343  171
             ResourceBundle resources = ((OperationDescriptor)descriptor).getResourceBundle(locale);
 344  171
             if (resources != null) try {
 345  171
                 return resources.getString("LocalName");
 346  0
             } catch (MissingResourceException exception) {
 347  
                 // No localized name. Fallback on the default (non-localized) descriptor name.
 348  
                 // No warning to report here, this exception is really not a problem.
 349  
             }
 350  
         }
 351  8
         return descriptor.getName();
 352  
     }
 353  
 
 354  
     /**
 355  
      * The tree cell renderer, which select icons according the selected object type.
 356  
      *
 357  
      * @author Martin Desruisseaux (IRD)
 358  
      * @version 3.00
 359  
      *
 360  
      * @since 2.3
 361  
      * @module
 362  
      */
 363  
     @SuppressWarnings("serial")
 364  1
     private static final class CellRenderer extends DefaultTreeCellRenderer {
 365  
         /** The icon for folder. */
 366  
         private final Icon open, closed;
 367  
 
 368  
         /** The icon for an operation, or {@code null} if none. */
 369  
         private static Icon operation;
 370  
 
 371  
         /** The icon for parameters, or {@code null} if none. */
 372  
         private static Icon parameter;
 373  
 
 374  
         /** The icon for implementations, or {@code null} if none. */
 375  
         private static Icon implementation;
 376  
 
 377  
         /**
 378  
          * Creates a cell renderer.
 379  
          */
 380  1
         private CellRenderer() {
 381  1
             open   = getDefaultOpenIcon();
 382  1
             closed = getDefaultClosedIcon();
 383  1
             if (operation == null) {
 384  1
                 final IconFactory icons = IconFactory.DEFAULT;
 385  1
                 operation      = icons.getIcon("crystalProject/16/apps/background.png");
 386  1
                 parameter      = icons.getIcon("toolbarButtonGraphics/general/Preferences16.gif");
 387  1
                 implementation = icons.getIcon("crystalProject/16/actions/run.png");
 388  
             }
 389  1
         }
 390  
 
 391  
         /**
 392  
          * Configures the renderer based on the passed in components.
 393  
          */
 394  
         @Override
 395  
         public Component getTreeCellRendererComponent(final JTree tree, final Object value,
 396  
                 final boolean selelected, final boolean expanded,
 397  
                 final boolean leaf, final int row, final boolean hasFocus)
 398  
         {
 399  
             final boolean isOp;
 400  16
             isOp = ((TreeNode) value).getUserObject() instanceof RegistryElementDescriptor;
 401  16
             Icon icon = isOp ? operation : open;
 402  16
             if (icon != null) {
 403  16
                 setOpenIcon(icon);
 404  
             }
 405  16
             icon = isOp ? operation : closed;
 406  16
             if (icon != null) {
 407  16
                 setClosedIcon(icon);
 408  
             }
 409  16
             icon = (value instanceof NamedTreeNode ? implementation : parameter);
 410  16
             if (icon != null) {
 411  16
                 setLeafIcon(icon);
 412  
             }
 413  16
             return super.getTreeCellRendererComponent(tree, value, selected, expanded,
 414  
                     leaf, row, hasFocus);
 415  
         }
 416  
     }
 417  
 }