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;
018:
019: // J2SE dependencies
020: import java.util.Map;
021: import java.util.Date;
022: import java.util.HashMap;
023: import java.util.ResourceBundle;
024: import java.util.MissingResourceException;
025: import java.util.logging.Level;
026: import java.lang.reflect.Array;
027: import java.text.ParseException;
028: import java.text.NumberFormat;
029: import java.text.DateFormat;
030: import java.text.Format;
031:
032: import javax.swing.JList; // For javadoc
033: import javax.swing.JTable;
034: import javax.swing.JLabel;
035: import javax.swing.JPanel;
036: import javax.swing.JTextArea;
037: import javax.swing.JTextField;
038: import javax.swing.JScrollPane;
039: import javax.swing.JFormattedTextField;
040: import javax.swing.BorderFactory;
041: import javax.swing.table.TableModel;
042: import javax.swing.table.AbstractTableModel;
043: import java.awt.geom.AffineTransform;
044: import java.awt.image.DataBuffer;
045: import java.awt.GridBagConstraints;
046: import java.awt.GridBagLayout;
047: import java.awt.BorderLayout;
048: import java.awt.CardLayout;
049: import java.awt.Container;
050: import java.awt.Component;
051: import java.awt.Dimension;
052:
053: // JAI dependencies
054: import javax.media.jai.util.Range;
055: import javax.media.jai.KernelJAI;
056: import javax.media.jai.LookupTableJAI;
057: import javax.media.jai.OperationNode;
058: import javax.media.jai.OperationDescriptor;
059: import javax.media.jai.PerspectiveTransform;
060: import javax.media.jai.ParameterListDescriptor;
061: import javax.media.jai.RegistryElementDescriptor;
062:
063: // Geotools dependencies
064: import org.geotools.measure.Angle;
065: import org.geotools.measure.AngleFormat;
066: import org.geotools.util.logging.Logging;
067: import org.geotools.resources.XMath;
068: import org.geotools.resources.Utilities;
069: import org.geotools.resources.i18n.Vocabulary;
070: import org.geotools.resources.i18n.VocabularyKeys;
071: import org.geotools.gui.swing.image.KernelEditor;
072:
073: /**
074: * An editor for arbitrary parameter object. The parameter value can be any {@link Object}.
075: * The editor content will changes according the parameter class. For example, the content
076: * will be a {@link KernelEditor} if the parameter is an instance of {@link KernelJAI}.
077: * Currently supported parameter type includes:
078: *
079: * <ul>
080: * <li>Individual {@linkplain String string}, {@linkplain Number number}, {@linkplain Date date}
081: * or {@linkplain Angle angle}.</li>
082: * <li>Table of any primitive type ({@code int[]}, {@code float[]}, etc.).</li>
083: * <li>Matrix of any primitive type ({@code int[][]}, {@code float[][]}, etc.).</li>
084: * <li>JAI {@linkplain LookupTableJAI lookup table}, which are display in tabular format.</li>
085: * <li>{@linkplain AffineTransform Affine transform} and {@linkplain PerspectiveTransform
086: * perspective transform}, which are display like a matrix.</li>
087: * <li>Convolution {@linkplain KernelJAI kernel}, which are display in a {@link KernelEditor}.</li>
088: * </ul>
089: *
090: * @since 2.0
091: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/extension/widgets-swing/src/main/java/org/geotools/gui/swing/ParameterEditor.java $
092: * @version $Id: ParameterEditor.java 27862 2007-11-12 19:51:19Z desruisseaux $
093: * @author Martin Desruisseaux
094: *
095: * @see org.geotools.gui.swing.image.KernelEditor
096: * @see org.geotools.gui.swing.image.ImageProperties
097: * @see org.geotools.gui.swing.image.OperationTreeBrowser
098: *
099: * @todo This class do not yet support the edition of parameter value.
100: * We will allow that in a future version. This work is already
101: * partially done with the 'editable' boolean value.
102: */
103: public class ParameterEditor extends JPanel {
104: /** Key for {@link String} node. */
105: private static final String STRING = "String";
106: /** Key for {@link Boolean} node. */
107: private static final String BOOLEAN = "Boolean";
108: /** Key for {@link Number} node. */
109: private static final String NUMBER = "Number";
110: /** Key for {@link Angle} node. */
111: private static final String ANGLE = "Angle";
112: /** Key for {@link Date} node. */
113: private static final String DATE = "Date";
114: /** Key for {@link KernelJAI} node. */
115: private static final String KERNEL = "Kernel";
116: /** Key for any kind of table node. */
117: private static final String TABLE = "Table";
118: /** Key for unrecognized types. */
119: private static final String DEFAULT = "Default";
120:
121: /**
122: * The set of {@linkplain Component component} editors created up to date.
123: * Keys are {@link String} and values are {@link Component} objects.
124: */
125: private final Map editors = new HashMap();
126:
127: /**
128: * The properties panel for parameters. The content for this panel
129: * depends on the selected item, but usually includes the following:
130: * <ul>
131: * <li>A {@link JTextField} for simple parameters (numbers, string, etc.)</li>
132: * <li>A {@link JList} for enumerated parameters.</li>
133: * <li>A {@link JTable} for any kind of array parameter and {@link LookupTableJAI}.</li>
134: * <li>A {@link KernelEditor} for {@link KernelJAI} parameters.</li>
135: * </ul>
136: */
137: private final Container cards = new JPanel(new CardLayout());
138:
139: /**
140: * The label for parameter or image description.
141: * Usually displayed on top of parameter editor.
142: */
143: private final JLabel description = new JLabel(" ", JLabel.CENTER);
144:
145: /**
146: * The current value in the process of being edited. This object is usually an instance of
147: * {@link Number}, {@link KernelJAI}, {@link LookupTableJAI} or some other parameter object.
148: *
149: * @see #setParameterValue
150: */
151: private Object value;
152:
153: /**
154: * The editor widget currently in use.
155: *
156: * @see #setParameterValue
157: * @see #getEditor
158: */
159: private Component editor;
160:
161: /**
162: * The editor model currently in use. This is often the model used by the editor widget.
163: */
164: private Editor model;
165:
166: /**
167: * {@code true} if this widget is editable.
168: */
169: private static final boolean editable = false;
170:
171: /**
172: * Constructs an initially empty parameter editor.
173: */
174: public ParameterEditor() {
175: super (new BorderLayout());
176: description.setBorder(BorderFactory.createCompoundBorder(
177: description.getBorder(),
178: BorderFactory.createCompoundBorder(BorderFactory
179: .createCompoundBorder(BorderFactory
180: .createEmptyBorder(6, 9, 6, 9),
181: BorderFactory
182: .createLineBorder(description
183: .getForeground())),
184: BorderFactory.createEmptyBorder(6, 0, 6, 0))));
185: add(description, BorderLayout.NORTH);
186: add(cards, BorderLayout.CENTER);
187: setPreferredSize(new Dimension(400, 250));
188: }
189:
190: /**
191: * Returns the parameter value currently edited, or {@code null} if none.
192: */
193: public Object getParameterValue() {
194: return (model != null) ? model.getValue() : value;
195: }
196:
197: /**
198: * Set the value to edit. The editor content will be updated according the value type.
199: * For example if the value is an instance of {@link KernelJAI}, then the editor content
200: * will be changed to a {@link KernelEditor}.
201: *
202: * @param value The value to edit. This object is usually an instance of {@link Number},
203: * {@link KernelJAI}, {@link LookupTableJAI} or some other parameter object.
204: */
205: public void setParameterValue(final Object value) {
206: final Object oldValue = this .value;
207: if (!Utilities.equals(value, oldValue)) {
208: this .value = value;
209: updateEditor();
210: firePropertyChange("value", oldValue, value);
211: }
212: }
213:
214: /**
215: * Returns the description currently shown, or {@code null} if none.
216: */
217: public String getDescription() {
218: String text = description.getText();
219: if (text != null) {
220: text = text.trim();
221: if (text.length() == 0) {
222: text = null;
223: }
224: }
225: return text;
226: }
227:
228: /**
229: * Set the description string to write on top of the editor.
230: * This is usually a short description of the paramter being edited.
231: */
232: public void setDescription(String description) {
233: if (description == null || description.length() == 0) {
234: description = " ";
235: }
236: this .description.setText(description);
237: if (model != null) {
238: model.setValueRange(null, null);
239: }
240: }
241:
242: /**
243: * Convenience method for setting the parameter description from a JAI operation node.
244: *
245: * @param operation The operation node for the current parameter.
246: * @param index The parameter index, or {@code -1} if unknow.
247: *
248: * @since 2.3
249: */
250: public void setDescription(final OperationNode operation,
251: final int index) {
252: String description = null;
253: Class type = null;
254: Range range = null;
255: if (operation != null) {
256: final String name, mode;
257: final RegistryElementDescriptor element;
258: final ParameterListDescriptor param;
259: name = operation.getOperationName();
260: mode = operation.getRegistryModeName();
261: element = operation.getRegistry().getDescriptor(mode, name);
262: param = element.getParameterListDescriptor(mode);
263: /*
264: * If a parameter is specified, gets the parameter type and its range of valid
265: * values.
266: */
267: if (index >= 0 && index < param.getNumParameters()) {
268: type = param.getParamClasses()[index];
269: range = param
270: .getParamValueRange(param.getParamNames()[index]);
271: }
272: /*
273: * If the descriptor is an operation, gets the localized operation
274: * description or the parameter description.
275: */
276: if (element instanceof OperationDescriptor) {
277: final String key;
278: final OperationDescriptor descriptor = (OperationDescriptor) element;
279: final ResourceBundle resources = descriptor
280: .getResourceBundle(getLocale());
281: if (index >= 0) {
282: key = "arg" + index + "Desc";
283: } else {
284: key = "Description";
285: }
286: try {
287: description = resources.getString(key);
288: } catch (MissingResourceException ignore) {
289: // No description for this parameter. Try a global description.
290: try {
291: description = resources
292: .getString("Description");
293: } catch (MissingResourceException exception) {
294: /*
295: * No description at all for this operation. Not a big deal;
296: * just left the description empty. Log the exception with a
297: * low level, since this warning is not really important. The
298: * level is slightly higher than in 'RegisteredOperationBrowser'
299: * since we have tried the global operation description as well.
300: */
301: Logging
302: .getLogger("org.geotools.gui.swing")
303: .log(
304: Level.FINE,
305: exception.getLocalizedMessage(),
306: exception);
307: }
308: }
309: }
310: }
311: setDescription(description);
312: if (model != null) {
313: model.setValueRange(type, range);
314: }
315: }
316:
317: /**
318: * Returns the component used for editing the parameter. The component class depends on the
319: * class of the value set by the last call to {@link #setParameterValue}. The editor may be
320: * an instance of {@link KernelEditor}, {@link JTable}, {@link JTextField}, {@link JList} or
321: * any other suitable component.
322: *
323: * @return The editor, or {@code null} if no value has been set.
324: */
325: public Component getEditor() {
326: return editor;
327: }
328:
329: /**
330: * Returns the editor for the given name. If an editor is found, it will be bring
331: * on top of the card layout (i.e. will become the visible editor). Otherwise, this
332: * method returns {@code null}.
333: *
334: * @param name The editor name. Should be one of {@link #NUMBER}, {@link #KERNEL} and
335: * similar constants.
336: * @return The editor, or {@code null}.
337: */
338: private Component getEditor(final String name) {
339: final Component panel = (Component) editors.get(name);
340: ((CardLayout) cards.getLayout()).show(cards, name);
341: return panel;
342: }
343:
344: /**
345: * Add the specified editor. No editor must exists for the specified name prior to this
346: * call. The editor will be bring on top of the card layout (i.e. will become the visible
347: * panel).
348: *
349: * @param name The editor name. Should be one of {@link #NUMBER}, {@link #KERNEL} and
350: * similar constants.
351: * @param editor The editor.
352: * @param scroll {@code true} if the editor should be wrapped into a {@link JScrollPane}
353: * prior its addition to the container.
354: */
355: private void addEditor(final String name, Component editor,
356: final boolean scroll) {
357: if (editors.put(name, editor) != null) {
358: throw new IllegalStateException(name); // Should not happen.
359: }
360: if (scroll) {
361: editor = new JScrollPane(editor);
362: }
363: cards.add(editor, name);
364: ((CardLayout) cards.getLayout()).show(cards, name);
365: }
366:
367: /**
368: * Update the editor according the current {@link #value}. If a suitable editors already
369: * exists for the value class, it will be reused. Otherwise, a new editor will be created
370: * on the fly.
371: *
372: * The {@link #editor} field will be set to the component used for editing the parameter.
373: * This component may be an instance of {@link KernelEditor}, {@link JTable},
374: * {@link JTextField}, {@link JList} or any other suitable component.
375: *
376: * The {@link #model} field will be set to the model used by the editor widget.
377: */
378: private void updateEditor() {
379: Object value = this .value;
380: /*
381: * In the special case where the value is an array with only one element, extract
382: * the element and use a specialized editor as if the element wasn't in an array.
383: */
384: while (value != null && value.getClass().isArray()
385: && Array.getLength(value) == 1) {
386: value = Array.get(value, 0);
387: }
388: /*
389: * String --- Uses a JTextField editor.
390: */
391: if (value instanceof String) {
392: Singleton editor = (Singleton) getEditor(STRING);
393: if (editor == null) {
394: editor = new Singleton(null);
395: addEditor(STRING, editor, false);
396: }
397: editor.setValue(value);
398: this .editor = editor.field;
399: this .model = editor;
400: return;
401: }
402: /*
403: * Boolean --- Uses a JTextField editor.
404: */
405: if (value instanceof Boolean) {
406: Singleton editor = (Singleton) getEditor(BOOLEAN);
407: if (editor == null) {
408: editor = new Singleton(null); // TODO: we should define some kind of BooleanFormat.
409: addEditor(BOOLEAN, editor, false);
410: }
411: editor.setValue(value);
412: this .editor = editor.field;
413: this .model = editor;
414: return;
415: }
416: /*
417: * Number --- Uses a JFormattedTextField editor.
418: */
419: if (value instanceof Number) {
420: Singleton editor = (Singleton) getEditor(NUMBER);
421: if (editor == null) {
422: editor = new Singleton(NumberFormat
423: .getInstance(getLocale()));
424: addEditor(NUMBER, editor, false);
425: }
426: editor.setValue(value);
427: this .editor = editor.field;
428: this .model = editor;
429: return;
430: }
431: /*
432: * Date --- Uses a JFormattedTextField editor.
433: */
434: if (value instanceof Date) {
435: Singleton editor = (Singleton) getEditor(DATE);
436: if (editor == null) {
437: editor = new Singleton(DateFormat.getDateTimeInstance(
438: DateFormat.LONG, DateFormat.LONG, getLocale()));
439: addEditor(DATE, editor, false);
440: }
441: editor.setValue(value);
442: this .editor = editor.field;
443: this .model = editor;
444: return;
445: }
446: /*
447: * Angle --- Uses a JFormattedTextField editor.
448: */
449: if (value instanceof Angle) {
450: Singleton editor = (Singleton) getEditor(ANGLE);
451: if (editor == null) {
452: editor = new Singleton(new AngleFormat("D°MM.mm'",
453: getLocale()));
454: addEditor(ANGLE, editor, false);
455: }
456: editor.setValue(value);
457: this .editor = editor.field;
458: this .model = editor;
459: return;
460: }
461: /*
462: * AffineTransform --- convert to a matrix for processing by the general matrix case.
463: */
464: if (value instanceof AffineTransform) {
465: final AffineTransform transform = (AffineTransform) value;
466: value = new double[][] {
467: { transform.getScaleX(), transform.getShearX(),
468: transform.getTranslateX() },
469: { transform.getShearY(), transform.getScaleY(),
470: transform.getTranslateY() }, { 0, 0, 1 } };
471: }
472: /*
473: * PerspectiveTransform --- convert to a matrix for processing by the general matrix case.
474: */
475: if (value instanceof PerspectiveTransform) {
476: final double[][] matrix = new double[][] { new double[3],
477: new double[3], new double[3] };
478: ((PerspectiveTransform) value).getMatrix(matrix);
479: value = matrix;
480: }
481: /*
482: * Any table or matrix --- use a JTable editor.
483: */
484: if (value != null) {
485: final Class elementClass = value.getClass()
486: .getComponentType();
487: if (elementClass != null) {
488: final TableModel model;
489: if (elementClass.isArray()) {
490: model = new Matrix((Object[]) value);
491: } else {
492: model = new Table(new Object[] { value }, 0, false);
493: }
494: JTable editor = (JTable) getEditor(TABLE);
495: if (editor == null) {
496: addEditor(TABLE, editor = new JTable(model), true);
497: } else {
498: editor.setModel(model);
499: }
500: this .editor = editor;
501: this .model = (Editor) model;
502: return;
503: }
504: }
505: /*
506: * LookupTableJAI --- Uses a JTable editor.
507: */
508: if (value instanceof LookupTableJAI) {
509: final LookupTableJAI table = (LookupTableJAI) value;
510: final Object[] data;
511: boolean unsigned = false;
512: switch (table.getDataType()) {
513: case DataBuffer.TYPE_BYTE:
514: data = table.getByteData();
515: unsigned = true;
516: break;
517: case DataBuffer.TYPE_USHORT:
518: unsigned = true; // Fall through
519: case DataBuffer.TYPE_SHORT:
520: data = table.getShortData();
521: break;
522: case DataBuffer.TYPE_INT:
523: data = table.getIntData();
524: break;
525: case DataBuffer.TYPE_FLOAT:
526: data = table.getFloatData();
527: break;
528: case DataBuffer.TYPE_DOUBLE:
529: data = table.getDoubleData();
530: break;
531: default:
532: this .editor = null;
533: this .model = null;
534: return;
535: }
536: final Table model = new Table(data, table.getOffset(),
537: unsigned);
538: JTable editor = (JTable) getEditor(TABLE);
539: if (editor == null) {
540: addEditor(TABLE, editor = new JTable(model), true);
541: } else {
542: editor.setModel(model);
543: }
544: this .editor = editor;
545: this .model = model;
546: return;
547: }
548: /*
549: * KernelJAI --- Uses a KernelEditor.
550: */
551: if (value instanceof KernelJAI) {
552: KernelEditor editor = (KernelEditor) getEditor(KERNEL);
553: if (editor == null) {
554: editor = new KernelEditor();
555: editor.addDefaultKernels();
556: addEditor(KERNEL, editor, false);
557: }
558: editor.setKernel((KernelJAI) value);
559: this .editor = editor;
560: this .model = null; // TODO: Set the editor.
561: return;
562: }
563: /*
564: * Default case --- Uses a JTextArea
565: */
566: JTextArea editor = (JTextArea) getEditor(DEFAULT);
567: if (editor == null) {
568: addEditor(DEFAULT, editor = new JTextArea(), true);
569: editor.setEditable(false);
570: }
571: editor.setText(String.valueOf(value));
572: this .editor = editor;
573: this .model = null; // TODO: Set the editor.
574: }
575:
576: /**
577: * The interface for editor capable to returns the edited value.
578: *
579: * @version $Id: ParameterEditor.java 27862 2007-11-12 19:51:19Z desruisseaux $
580: * @author Martin Desruisseaux
581: *
582: * @todo This interface should have a 'setEditable(boolean)' method.
583: */
584: private static interface Editor {
585: /**
586: * Returns the edited value.
587: */
588: public abstract Object getValue();
589:
590: /**
591: * Set the type and the range of valid values.
592: */
593: public abstract void setValueRange(final Class type,
594: final Range range);
595: }
596:
597: /**
598: * An editor panel for editing a single value. The value if usually an instance of
599: * {@link Number}, {@link Date}, {@link Angle}, {@link Boolean} or {@link String}.
600: *
601: * @version $Id: ParameterEditor.java 27862 2007-11-12 19:51:19Z desruisseaux $
602: * @author Martin Desruisseaux
603: *
604: * @todo This editor should use {@code JSpinner}, but we need to gets
605: * the minimum and maximum values first since spinner needs bounds.
606: */
607: private static final class Singleton extends JPanel implements
608: Editor {
609: /**
610: * The data type.
611: */
612: private final JLabel type = new JLabel();
613:
614: /**
615: * The minimum allowed value.
616: */
617: private final JLabel minimum = new JLabel();
618:
619: /**
620: * The maximum allowed value.
621: */
622: private final JLabel maximum = new JLabel();
623:
624: /**
625: * The text field for editing the value.
626: */
627: private final JTextField field;
628:
629: /**
630: * Construct an editor for value using the specified format.
631: */
632: public Singleton(final Format format) {
633: super (new GridBagLayout());
634: if (format != null) {
635: field = new JFormattedTextField(format);
636: } else {
637: field = new JTextField();
638: }
639: field.setEditable(editable);
640: final Vocabulary resources = Vocabulary
641: .getResources(getLocale());
642: final GridBagConstraints c = new GridBagConstraints();
643: c.gridx = 0;
644: c.gridwidth = 1;
645: c.insets.left = 9;
646: c.fill = c.HORIZONTAL;
647: c.gridy = 0;
648: add(new JLabel(resources.getLabel(VocabularyKeys.TYPE)), c);
649: c.gridy++;
650: add(new JLabel(resources.getLabel(VocabularyKeys.MINIMUM)),
651: c);
652: c.gridy++;
653: add(new JLabel(resources.getLabel(VocabularyKeys.MAXIMUM)),
654: c);
655: c.gridy++;
656: add(new JLabel(resources.getLabel(VocabularyKeys.VALUE)), c);
657: c.gridx = 1;
658: c.weightx = 1;
659: c.insets.right = 9;
660: c.gridy = 0;
661: add(type, c);
662: c.gridy++;
663: add(minimum, c);
664: c.gridy++;
665: add(maximum, c);
666: c.gridy++;
667: add(field, c);
668: }
669:
670: /**
671: * Set the value to be edited.
672: */
673: public void setValue(final Object value) {
674: if (field instanceof JFormattedTextField) {
675: ((JFormattedTextField) field).setValue(value);
676: } else {
677: field.setText(String.valueOf(value));
678: }
679: }
680:
681: /**
682: * Returns the edited value.
683: */
684: public Object getValue() {
685: if (field instanceof JFormattedTextField) {
686: return ((JFormattedTextField) field).getValue();
687: } else {
688: return field.getText();
689: }
690: }
691:
692: /**
693: * Set the type and the range of valid values.
694: */
695: public void setValueRange(Class classe, final Range range) {
696: String type = null;
697: String minimum = null;
698: String maximum = null;
699: if (classe != null) {
700: while (classe.isArray()) {
701: classe = classe.getComponentType();
702: }
703: classe = XMath.primitiveToWrapper(classe);
704: boolean isInteger = false;
705: if (XMath.isReal(classe)
706: || (isInteger = XMath.isInteger(classe)) == true) {
707: type = Vocabulary
708: .format(
709: isInteger ? VocabularyKeys.SIGNED_INTEGER_$1
710: : VocabularyKeys.REAL_NUMBER_$1,
711: new Integer(XMath
712: .getBitCount(classe)));
713: } else {
714: type = Utilities.getShortName(classe);
715: }
716: }
717: if (range != null) {
718: minimum = format(range.getMinValue());
719: maximum = format(range.getMaxValue());
720: }
721: this .type.setText(type);
722: this .minimum.setText(minimum);
723: this .maximum.setText(maximum);
724: }
725:
726: /**
727: * Format the given value.
728: */
729: private String format(final Comparable value) {
730: if (value == null) {
731: return null;
732: }
733: if (field instanceof JFormattedTextField)
734: try {
735: return ((JFormattedTextField) field).getFormatter()
736: .valueToString(value);
737: } catch (ParseException exception) {
738: // Value can't be formatted. Fall back on the 'toString()' method, which
739: // is okay since this string is used for informative purpose only.
740: }
741: return value.toString();
742: }
743: }
744:
745: /**
746: * Table model for table parameters (including {@link LookupTableJAI}.
747: * Instance of this class are created by {@link #updateEditor} when first needed.
748: *
749: * @version $Id: ParameterEditor.java 27862 2007-11-12 19:51:19Z desruisseaux $
750: * @author Martin Desruisseaux
751: */
752: private static final class Table extends AbstractTableModel
753: implements Editor {
754: /**
755: * The table (usually an instance of {@code double[][]}).
756: */
757: private final Object[] table;
758:
759: /**
760: * The offset parameter (a {@link LookupTableJAI} property).
761: */
762: private final int offset;
763:
764: /**
765: * {@code true} if the table values are unsigned.
766: */
767: private final boolean unsigned;
768:
769: /**
770: * Constructs a model for the given table.
771: *
772: * @param table The table (usually an instance of {@code double[][]}).
773: * @param offset The offset parameter (a {@link LookupTableJAI} property).
774: * @param unsigned {@code true} if the table values are unsigned.
775: */
776: public Table(final Object[] table, final int offset,
777: final boolean unsigned) {
778: this .table = table;
779: this .offset = offset;
780: this .unsigned = unsigned;
781: }
782:
783: /**
784: * Returns the number of rows in the table.
785: */
786: public int getRowCount() {
787: int count = 0;
788: for (int i = 0; i < table.length; i++) {
789: final int length = Array.getLength(table[i]);
790: if (length > count) {
791: count = length;
792: }
793: }
794: return count;
795: }
796:
797: /**
798: * Returns the number of columns in the model.
799: */
800: public int getColumnCount() {
801: return Array.getLength(table) + 1;
802: }
803:
804: /**
805: * Returns the name of the column at the specified index.
806: */
807: public String getColumnName(final int index) {
808: switch (index) {
809: case 0:
810: return Vocabulary.format(VocabularyKeys.INDEX);
811: default:
812: return Vocabulary.format(VocabularyKeys.VALUE);
813: }
814: }
815:
816: /**
817: * Returns the most specific superclass for all the cell values.
818: */
819: public Class getColumnClass(final int index) {
820: if (index == 0 || unsigned) {
821: return Integer.class;
822: }
823: return XMath.primitiveToWrapper(table[index - 1].getClass()
824: .getComponentType());
825: }
826:
827: /**
828: * Tells if the specified cell is editable.
829: */
830: public boolean isCellEditable(final int row, final int column) {
831: return editable && column != 0;
832: }
833:
834: /**
835: * Returns the value at the specified index.
836: */
837: public Object getValueAt(final int row, final int column) {
838: if (column == 0) {
839: return new Integer(row + offset);
840: }
841: final Object array = table[column - 1];
842: if (unsigned) {
843: return new Integer(
844: Array.getInt(array, row) & 0x7FFFFFFF);
845: }
846: return Array.get(array, row);
847: }
848:
849: /**
850: * Set the value at the given index.
851: */
852: public void setValueAt(final Object value, final int row,
853: final int column) {
854: Array.set(table[column - 1], row, value);
855: }
856:
857: /**
858: * Returns the edited value.
859: */
860: public Object getValue() {
861: return table;
862: }
863:
864: /**
865: * Set the type and the range of valid values.
866: * The default implementation does nothing.
867: */
868: public void setValueRange(final Class type, final Range range) {
869: }
870: }
871:
872: /**
873: * Table model for matrix parameters. Instance of this class
874: * are created by {@link #updateEditor} when first needed.
875: *
876: * @version $Id: ParameterEditor.java 27862 2007-11-12 19:51:19Z desruisseaux $
877: * @author Martin Desruisseaux
878: */
879: private static final class Matrix extends AbstractTableModel
880: implements Editor {
881: /**
882: * The matrix (usually an instance of {@code double[][]}).
883: */
884: private final Object[] matrix;
885:
886: /**
887: * Construct a model for the given matrix.
888: *
889: * @param matrix The matrix (usually an instance of {@code double[][]}).
890: */
891: public Matrix(final Object[] matrix) {
892: this .matrix = matrix;
893: }
894:
895: /**
896: * Returns the number of rows in the matrix.
897: */
898: public int getRowCount() {
899: return matrix.length;
900: }
901:
902: /**
903: * Returns the number of columns in the model. This is the length of the longest
904: * row in the matrix.
905: */
906: public int getColumnCount() {
907: int count = 0;
908: for (int i = 0; i < matrix.length; i++) {
909: final int length = Array.getLength(matrix[i]);
910: if (length > count) {
911: count = length;
912: }
913: }
914: return count;
915: }
916:
917: /**
918: * Returns the name of the column at the specified index.
919: */
920: public String getColumnName(final int index) {
921: return Integer.toString(index);
922: }
923:
924: /**
925: * Returns the most specific superclass for all the cell values.
926: */
927: public Class getColumnClass(final int index) {
928: return XMath.primitiveToWrapper(matrix.getClass()
929: .getComponentType().getComponentType());
930: }
931:
932: /**
933: * Tells if the specified cell is editable.
934: */
935: public boolean isCellEditable(final int row, final int column) {
936: return editable;
937: }
938:
939: /**
940: * Returns the value at the specified index.
941: */
942: public Object getValueAt(final int row, final int column) {
943: final Object array = matrix[row];
944: return (column < Array.getLength(array)) ? Array.get(array,
945: column) : null;
946: }
947:
948: /**
949: * Set the value at the given index.
950: */
951: public void setValueAt(final Object value, final int row,
952: final int column) {
953: Array.set(matrix[row], column, value);
954: }
955:
956: /**
957: * Returns the edited value.
958: */
959: public Object getValue() {
960: return matrix;
961: }
962:
963: /**
964: * Set the type and the range of valid values.
965: * The default implementation does nothing.
966: */
967: public void setValueRange(final Class type, final Range range) {
968: }
969: }
970: }
|