001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
005: * (C) 2003, Institut de Recherche pour le Développement
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: */
017: package org.geotools.gui.swing.image;
018:
019: // J2SE dependencies
020: import java.util.List;
021: import java.util.Arrays;
022: import java.util.Locale;
023: import java.util.Iterator;
024: import java.util.Comparator;
025: import java.util.Collections;
026: import java.util.ResourceBundle;
027: import java.util.MissingResourceException;
028: import java.util.logging.Level;
029:
030: import java.awt.Component;
031: import java.awt.BorderLayout;
032: import javax.swing.Icon;
033: import javax.swing.Box;
034: import javax.swing.JTree;
035: import javax.swing.JLabel;
036: import javax.swing.JPanel;
037: import javax.swing.JFrame;
038: import javax.swing.JScrollPane;
039: import javax.swing.BorderFactory;
040: import javax.swing.tree.TreePath;
041: import javax.swing.tree.TreeModel;
042: import javax.swing.tree.TreeSelectionModel;
043: import javax.swing.tree.DefaultTreeModel;
044: import javax.swing.tree.DefaultTreeCellRenderer;
045: import javax.swing.event.TreeSelectionEvent;
046: import javax.swing.event.TreeSelectionListener;
047: import java.awt.image.renderable.ParameterBlock; // For javadoc
048:
049: // JAI dependencies
050: import javax.media.jai.JAI;
051: import javax.media.jai.OperationRegistry;
052: import javax.media.jai.OperationDescriptor;
053: import javax.media.jai.ParameterListDescriptor;
054: import javax.media.jai.RegistryElementDescriptor;
055:
056: // Geotools dependencies
057: import org.geotools.resources.Arguments;
058: import org.geotools.resources.Utilities;
059: import org.geotools.gui.swing.IconFactory;
060: import org.geotools.gui.swing.tree.Trees;
061: import org.geotools.gui.swing.tree.TreeNode;
062: import org.geotools.gui.swing.tree.NamedTreeNode;
063: import org.geotools.gui.swing.tree.DefaultMutableTreeNode;
064: import org.geotools.resources.i18n.VocabularyKeys;
065: import org.geotools.resources.i18n.Vocabulary;
066: import org.geotools.util.logging.Logging;
067:
068: /**
069: * Browse through the registered JAI operations. This widget display a tree build from a
070: * JAI's {@link OperationRegistry}. The tree has the following hierarchy:
071: * <p>
072: * <ul>
073: * <li>At the first level, all {@linkplain OperationRegistry#getRegistryModes() registry modes}
074: * (e.g. "rendered", "renderable", etc.) in alphabetical order.</li>
075: * <li>At the second level, all {@linkplain OperationRegistry#getDescriptors(String) operation
076: * descriptors} (e.g. "Affine", "Multiply", etc.) registered in each
077: * registry mode, in alphabetical order. This is the operation name to be given to
078: * {@link JAI#create(String,ParameterBlock) JAI.create(...)} methods.</li>
079: * <li>At the third level, a list of
080: * {@linkplain RegistryElementDescriptor#getParameterListDescriptor(String) parameters}
081: * as leafs, and the list of
082: * {@linkplain OperationRegistry#getOrderedProductList implementing products} as nodes.
083: * This level is not sorted in alphabetical order, since the ordering is relevant.</li>
084: * <li>At the last level, a list of {@linkplain OperationRegistry#getOrderedFactoryList
085: * factories} as leafs. This level is not sorted in alphabetical order, since the ordering
086: * is relevant.</li>
087: * </ul>
088: *
089: * @since 2.3
090: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/extension/widgets-swing/src/main/java/org/geotools/gui/swing/image/RegisteredOperationBrowser.java $
091: * @version $Id: RegisteredOperationBrowser.java 27862 2007-11-12 19:51:19Z desruisseaux $
092: * @author Martin Desruisseaux
093: */
094: public class RegisteredOperationBrowser extends JPanel {
095: /**
096: * The text area for operation's description.
097: */
098: private final JLabel description = new JLabel(" ");
099:
100: /**
101: * The text area for the version and vendor.
102: */
103: private final JLabel version = new JLabel(" ");
104:
105: /**
106: * Constructs a new operation browser for the default {@link JAI} instance.
107: */
108: public RegisteredOperationBrowser() {
109: this (getTree());
110: }
111:
112: /**
113: * Constructs a new operation browser for the specified operation registry.
114: *
115: * @param registry The operation registry to use for fetching operations.
116: */
117: public RegisteredOperationBrowser(final OperationRegistry registry) {
118: this (getTree(registry, getDefaultLocale()));
119: }
120:
121: /**
122: * Constructs a new operation browser for operations from the specified tree.
123: *
124: * @param model The tree model built by one of {@link #getTree} methods.
125: */
126: private RegisteredOperationBrowser(final TreeModel model) {
127: super (new BorderLayout());
128: final JTree tree = new JTree(model);
129: tree.setBorder(BorderFactory.createEmptyBorder(6, 6, 0, 0));
130: add(new JScrollPane(tree), BorderLayout.CENTER);
131: /*
132: * Add labels (description and version number).
133: */
134: final Box labels = Box.createVerticalBox();
135: labels.add(description);
136: labels.add(version);
137: labels.setBorder(BorderFactory.createEmptyBorder(3, 6, 3, 0));
138: add(labels, BorderLayout.SOUTH);
139: /*
140: * Configure the operations tree.
141: */
142: tree.getSelectionModel().setSelectionMode(
143: TreeSelectionModel.SINGLE_TREE_SELECTION);
144: tree.addTreeSelectionListener(new TreeSelectionListener() {
145: public void valueChanged(final TreeSelectionEvent event) {
146: selected(event.getNewLeadSelectionPath());
147: }
148: });
149: /*
150: * Set icons (optional)
151: */
152: tree.setCellRenderer(new CellRenderer());
153: }
154:
155: /**
156: * Invoked when the user selected a new operation in the tree. This method find the
157: * {@link OperationDescriptor} for the selected node and invokes {@link #selected}.
158: *
159: * @param path The selected tree path, or {@code null} if none.
160: */
161: private void selected(final TreePath path) {
162: if (path != null) {
163: for (int i = path.getPathCount(); --i >= 0;) {
164: final Object component = path.getPathComponent(i);
165: Object candidate = component;
166: if (candidate instanceof TreeNode) {
167: candidate = ((TreeNode) candidate).getUserObject();
168: /*
169: * Note: The missing 'getUserObject()' method is fixed
170: * in Geotools TreeNode, not the Swing one...
171: */
172: }
173: if (candidate instanceof OperationDescriptor) {
174: int index = -1;
175: /*
176: * Fetch the parameter index. Note: the Swing TreeNode is suffisient
177: * for this task (no need for the fixed Geotools's TreeNode).
178: */
179: if (component instanceof javax.swing.tree.TreeNode) {
180: final javax.swing.tree.TreeNode node = (javax.swing.tree.TreeNode) component;
181: final Object leaf = path.getLastPathComponent();
182: for (index = node.getChildCount(); --index >= 0;) {
183: final javax.swing.tree.TreeNode param = node
184: .getChildAt(index);
185: if (param == leaf
186: && !param.getAllowsChildren()) {
187: break;
188: }
189: }
190: }
191: selected((OperationDescriptor) candidate, index);
192: return;
193: }
194: }
195: }
196: selected(null, -1);
197: }
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,
209: final int param) {
210: String description = " ";
211: String version = " ";
212: if (operation != null) {
213: final String key;
214: final Locale locale = getLocale();
215: final ResourceBundle resources = operation
216: .getResourceBundle(locale);
217: if (param >= 0) {
218: key = "arg" + param + "Desc";
219: } else {
220: key = "Description";
221: }
222: try {
223: description = resources.getString(key);
224: version = Vocabulary.getResources(locale).getString(
225: VocabularyKeys.VERSION_$1,
226: resources.getString("Version"))
227: + ", " + resources.getString("Vendor");
228: } catch (MissingResourceException exception) {
229: /*
230: * A description was missing for this operation or parameter. This is not a big
231: * deal; just left some label blank. Log the exception with a low level, since
232: * this warning is not really important.
233: */
234: Logging.getLogger("org.geotools.gui.swing").log(
235: Level.FINER, exception.getLocalizedMessage(),
236: exception);
237: }
238: }
239: this .description.setText(description);
240: this .version.setText(version);
241: }
242:
243: /**
244: * Returns a tree view of all operations registered in the default {@link JAI} instance.
245: * Labels will be formatted in the Swing's {@linkplain #getDefaultLocale default locale}.
246: *
247: * @return All JAI operations as a tree.
248: */
249: public static TreeModel getTree() {
250: return getTree(JAI.getDefaultInstance().getOperationRegistry(),
251: getDefaultLocale());
252: }
253:
254: /**
255: * Returns a tree view of all operations registered in the given registry.
256: *
257: * @param registry The registry (e.g. {@link JAI#getOperationRegistry()}).
258: * @param locale The locale (e.g. {@link Locale#getDefault()}).
259: * @return All JAI operations as a tree.
260: *
261: * @see #getTree()
262: * @see JAI#getDefaultInstance()
263: * @see JAI#getOperationRegistry()
264: * @see Locale#getDefault()
265: */
266: public static TreeModel getTree(final OperationRegistry registry,
267: final Locale locale) {
268: final Vocabulary resources = Vocabulary.getResources(locale);
269: final DefaultMutableTreeNode root = new DefaultMutableTreeNode(
270: resources.getString(VocabularyKeys.OPERATIONS));
271: /*
272: * Add registry modes ("rendered", "renderable", etc.),
273: * and gets the operation descriptors for each mode.
274: */
275: final String[] modes = registry.getRegistryModes();
276: Arrays.sort(modes);
277: for (int i = 0; i < modes.length; i++) {
278: final String mode = modes[i];
279: final DefaultMutableTreeNode modeNode = new DefaultMutableTreeNode(
280: mode);
281: final List descriptors/*<RegistryElementDescriptor>*/= registry
282: .getDescriptors(mode);
283: Collections.sort(descriptors, new Comparator() {
284: public int compare(final Object obj1, final Object obj2) {
285: final RegistryElementDescriptor desc1 = (RegistryElementDescriptor) obj1;
286: final RegistryElementDescriptor desc2 = (RegistryElementDescriptor) obj2;
287: return desc1.getName().compareTo(desc2.getName());
288: }
289: });
290: /*
291: * Add the operations ("add", "convolve", etc.) and their parameters.
292: */
293: for (final Iterator it = descriptors.iterator(); it
294: .hasNext();) {
295: final RegistryElementDescriptor descriptor;
296: final DefaultMutableTreeNode descriptorNode;
297: final ParameterListDescriptor param;
298: descriptor = (RegistryElementDescriptor) it.next();
299: descriptorNode = new NamedTreeNode(getName(descriptor,
300: locale), descriptor);
301: param = descriptor.getParameterListDescriptor(mode);
302: if (param != null) {
303: final String[] names = param.getParamNames();
304: if (names != null) {
305: // No sorting; the order is relevant
306: for (int j = 0; j < names.length; j++) {
307: // Should not be NamedTreeNode, because the later is used for
308: // differentiating parameters and implementations (see below).
309: descriptorNode
310: .add(new DefaultMutableTreeNode(
311: names[j], false));
312: }
313: }
314: }
315: /*
316: * Add the implementing products and the factories, if any.
317: */
318: final String operationName = descriptor.getName();
319: final List/*<String>*/products = registry
320: .getOrderedProductList(mode, operationName);
321: if (products != null) {
322: final DefaultMutableTreeNode productsNode;
323: productsNode = new DefaultMutableTreeNode(resources
324: .getString(VocabularyKeys.IMPLEMENTATIONS));
325: for (final Iterator itp = products.iterator(); itp
326: .hasNext();) {
327: final String product = (String) itp.next();
328: final DefaultMutableTreeNode productNode;
329: productNode = new DefaultMutableTreeNode(
330: product);
331:
332: final List factories;
333: factories = registry.getOrderedFactoryList(
334: mode, operationName, product);
335: if (factories != null) {
336: for (final Iterator itf = factories
337: .iterator(); itf.hasNext();) {
338: final Object factory = itf.next();
339: productNode
340: .add(new NamedTreeNode(
341: Utilities
342: .getShortClassName(factory),
343: factory, false));
344: // The node class (NamedTreeNode) should be different from the
345: // node for parameters (see above), in order to differentiate
346: // those leafs in the cell renderer.
347: }
348: }
349: productsNode.add(productNode);
350: }
351: descriptorNode.add(productsNode);
352: }
353: modeNode.add(descriptorNode);
354: }
355: root.add(modeNode);
356: }
357: return new DefaultTreeModel(root, true);
358: }
359:
360: /**
361: * Returns the localized name for the given descriptor. The name will be fecth from the
362: * "{@link OperationDescriptor#getResourceBundle LocalName}" resource, if available.
363: * Otherwise, the {@linkplain RegistryElementDescriptor#getName non-localized name} is returned.
364: */
365: private static String getName(
366: final RegistryElementDescriptor descriptor,
367: final Locale locale) {
368: if (descriptor instanceof OperationDescriptor) {
369: ResourceBundle resources = ((OperationDescriptor) descriptor)
370: .getResourceBundle(locale);
371: if (resources != null)
372: try {
373: return resources.getString("LocalName");
374: } catch (MissingResourceException exception) {
375: // No localized name. Fallback on the default (non-localized) descriptor name.
376: // No warning to report here, this exception is really not a problem.
377: }
378: }
379: return descriptor.getName();
380: }
381:
382: /**
383: * The tree cell renderer, which select icons according the selected object type.
384: */
385: private static final class CellRenderer extends
386: DefaultTreeCellRenderer {
387: /** The icon for folder. */
388: private final Icon open, closed;
389:
390: /** The icon for an operation, or {@code null} if none. */
391: private static Icon operation;
392:
393: /** The icon for parameters, or {@code null} if none. */
394: private static Icon parameter;
395:
396: /** The icon for implementations, or {@code null} if none. */
397: private static Icon implementation;
398:
399: /**
400: * Creates a cell renderer.
401: */
402: private CellRenderer() {
403: open = getDefaultOpenIcon();
404: closed = getDefaultClosedIcon();
405: if (operation == null) {
406: final IconFactory icons = IconFactory.DEFAULT;
407: operation = icons
408: .getIcon("toolbarButtonGraphics/general/Information16.gif");
409: parameter = icons
410: .getIcon("toolbarButtonGraphics/general/Preferences16.gif");
411: implementation = icons
412: .getIcon("toolbarButtonGraphics/general/About16.gif");
413: }
414: }
415:
416: /**
417: * Configures the renderer based on the passed in components.
418: */
419: public Component getTreeCellRendererComponent(final JTree tree,
420: final Object value, final boolean selelected,
421: final boolean expanded, final boolean leaf,
422: final int row, final boolean hasFocus) {
423: final boolean isOp;
424: isOp = ((TreeNode) value).getUserObject() instanceof RegistryElementDescriptor;
425: Icon icon = isOp ? operation : open;
426: if (icon != null) {
427: setOpenIcon(icon);
428: }
429: icon = isOp ? operation : closed;
430: if (icon != null) {
431: setClosedIcon(icon);
432: }
433: icon = (value instanceof NamedTreeNode ? implementation
434: : parameter);
435: if (icon != null) {
436: setLeafIcon(icon);
437: }
438: return super .getTreeCellRendererComponent(tree, value,
439: selected, expanded, leaf, row, hasFocus);
440: }
441: }
442:
443: /**
444: * Display the operation browser from the command line. This method is usefull for checking
445: * the widget appearance and the list of registered {@link JAI} operations. If this method
446: * is launch with the {@code -print} argument, then the tree of operations will be sent
447: * to standard output.
448: *
449: * @param args the command line arguments
450: */
451: public static void main(final String[] args) {
452: final Arguments arguments = new Arguments(args);
453: Locale.setDefault(arguments.locale);
454: if (arguments.getFlag("-print")) {
455: arguments.out.println(Trees.toString(getTree()));
456: } else {
457: final JFrame frame = new JFrame(Vocabulary
458: .format(VocabularyKeys.OPERATIONS));
459: frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
460: frame.getContentPane()
461: .add(new RegisteredOperationBrowser());
462: frame.pack();
463: frame.setVisible(true);
464: }
465: }
466: }
|