All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.metawidget.swing.widgetbuilder.SwingWidgetBuilder Maven / Gradle / Ivy

There is a newer version: 4.2
Show newest version
// Metawidget (licensed under LGPL)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package org.metawidget.swing.widgetbuilder;

import static org.metawidget.inspector.InspectionResultConstants.*;

import java.awt.Component;
import java.text.DecimalFormat;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPasswordField;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JSpinner;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.plaf.basic.BasicComboBoxEditor;
import javax.swing.plaf.basic.BasicComboBoxRenderer;
import javax.swing.text.JTextComponent;

import org.metawidget.swing.Stub;
import org.metawidget.swing.SwingMetawidget;
import org.metawidget.swing.SwingValuePropertyProvider;
import org.metawidget.swing.widgetprocessor.binding.BindingConverter;
import org.metawidget.util.ClassUtils;
import org.metawidget.util.CollectionUtils;
import org.metawidget.util.WidgetBuilderUtils;
import org.metawidget.widgetbuilder.iface.WidgetBuilder;

/**
 * WidgetBuilder for Swing environments.
 * 

* Creates native Swing JComponents, such as JTextField and * JComboBox, to suit the inspected fields. * * @author Richard Kennard */ public class SwingWidgetBuilder implements WidgetBuilder, SwingValuePropertyProvider { // // Public methods // public String getValueProperty( Component component ) { if ( component instanceof JComboBox ) { return "selectedItem"; } if ( component instanceof JTextComponent ) { return "text"; } if ( component instanceof JSpinner ) { return "value"; } if ( component instanceof JSlider ) { return "value"; } if ( component instanceof JCheckBox ) { return "selected"; } return null; } public JComponent buildWidget( String elementName, Map attributes, SwingMetawidget metawidget ) { // Hidden if ( TRUE.equals( attributes.get( HIDDEN ) ) ) { return new Stub(); } // Action if ( ACTION.equals( elementName ) ) { return new JButton( metawidget.getLabelString( attributes ) ); } Class clazz = WidgetBuilderUtils.getActualClassOrType( attributes, String.class ); // Support mandatory Booleans (can be rendered as a checkbox, even though they have a // Lookup) if ( Boolean.class.equals( clazz ) && TRUE.equals( attributes.get( REQUIRED ) ) ) { return new JCheckBox(); } // Lookups String lookup = attributes.get( LOOKUP ); if ( lookup != null && !"".equals( lookup ) ) { JComboBox comboBox = new JComboBox(); // Add an empty choice (if nullable, and not required) if ( WidgetBuilderUtils.needsEmptyLookupItem( attributes ) ) { comboBox.addItem( null ); } List values = CollectionUtils.fromString( lookup ); List convertedValues = CollectionUtils.newArrayList(); BindingConverter converter = metawidget.getWidgetProcessor( BindingConverter.class ); for ( String value : values ) { // Convert (if supported) Object convertedValue; if ( converter == null ) { convertedValue = value; } else { convertedValue = converter.convertFromString( value, clazz ); } comboBox.addItem( convertedValue ); convertedValues.add( convertedValue ); } // May have alternate labels String lookupLabels = attributes.get( LOOKUP_LABELS ); if ( lookupLabels != null && !"".equals( lookupLabels ) ) { Map labelsMap = CollectionUtils.newHashMap( convertedValues, CollectionUtils.fromString( attributes.get( LOOKUP_LABELS ) ) ); comboBox.setEditor( new LookupComboBoxEditor( labelsMap ) ); comboBox.setRenderer( new LookupComboBoxRenderer( labelsMap ) ); } return comboBox; } if ( clazz != null ) { // Primitives if ( clazz.isPrimitive() ) { // booleans if ( boolean.class.equals( clazz ) ) { return new JCheckBox(); } // chars if ( char.class.equals( clazz ) ) { return new JTextField(); } // Ranged String minimumValue = attributes.get( MINIMUM_VALUE ); String maximumValue = attributes.get( MAXIMUM_VALUE ); if ( minimumValue != null && !"".equals( minimumValue ) && maximumValue != null && !"".equals( maximumValue ) ) { JSlider slider = new JSlider(); slider.setMinimum( Integer.parseInt( minimumValue ) ); slider.setValue( slider.getMinimum() ); slider.setMaximum( Integer.parseInt( maximumValue ) ); return slider; } // Not-ranged Comparable minimum; Comparable maximum; if ( minimumValue != null && !"".equals( minimumValue ) ) { @SuppressWarnings( "unchecked" ) Comparable comparable = (Comparable) ClassUtils.parseNumber( clazz, minimumValue ); minimum = comparable; } else { @SuppressWarnings( "unchecked" ) Comparable comparable = (Comparable) ClassUtils.getNumberMinValue( clazz ); minimum = comparable; } if ( maximumValue != null && !"".equals( maximumValue ) ) { @SuppressWarnings( "unchecked" ) Comparable comparable = (Comparable) ClassUtils.parseNumber( clazz, maximumValue ); maximum = comparable; } else { @SuppressWarnings( "unchecked" ) Comparable comparable = (Comparable) ClassUtils.getNumberMaxValue( clazz ); maximum = comparable; } // Configurable step Number stepSize; if ( attributes.containsKey( MAXIMUM_FRACTIONAL_DIGITS ) ) { stepSize = Math.pow( 10, -Integer.parseInt( attributes.get( MAXIMUM_FRACTIONAL_DIGITS ) ) ); } else if ( float.class.equals( clazz ) || Float.class.equals( clazz ) ) { stepSize = 0.1f; } else if ( double.class.equals( clazz ) || Double.class.equals( clazz ) ) { stepSize = 0.1f; } else { stepSize = 1; } // Note it is very important we set the initial value of the JSpinner to the same // type as the property it maps to (eg. float or double, int or long). Number value = (Number) ClassUtils.parseNumber( clazz, "0" ); if ( minimum.compareTo( value ) > 0 ) { value = (Number) minimum; } else if ( maximum.compareTo( value ) < 0 ) { value = (Number) maximum; } JSpinner spinner = new JSpinner( new SpinnerNumberModel( value, minimum, maximum, stepSize ) ); // By default, a JSpinner calls setColumns. For numbers like Integer.MAX_VALUE and // Double.MAX_VALUE, this can be very large and mess up the layout. Here, we reset // setColumns to 0. JSpinner.NumberEditor editor = (JSpinner.NumberEditor) spinner.getEditor(); editor.getTextField().setColumns( 0 ); DecimalFormat format = editor.getFormat(); if ( attributes.containsKey( MINIMUM_FRACTIONAL_DIGITS ) ) { format.setMinimumFractionDigits( Integer.parseInt( attributes.get( MINIMUM_FRACTIONAL_DIGITS ) )); } if ( attributes.containsKey( MAXIMUM_FRACTIONAL_DIGITS ) ) { format.setMaximumFractionDigits( Integer.parseInt( attributes.get( MAXIMUM_FRACTIONAL_DIGITS ) )); } if ( attributes.containsKey( MINIMUM_INTEGER_DIGITS ) ) { format.setMinimumIntegerDigits( Integer.parseInt( attributes.get( MINIMUM_INTEGER_DIGITS ) )); } if ( attributes.containsKey( MAXIMUM_INTEGER_DIGITS ) ) { format.setMaximumIntegerDigits( Integer.parseInt( attributes.get( MAXIMUM_INTEGER_DIGITS ) )); } return spinner; } // Strings if ( String.class.equals( clazz ) ) { if ( TRUE.equals( attributes.get( MASKED ) ) ) { return new JPasswordField(); } if ( TRUE.equals( attributes.get( LARGE ) ) ) { JTextArea textarea = new JTextArea(); // Since we know we are dealing with Strings, we consider // word-wrapping a sensible default textarea.setLineWrap( true ); textarea.setWrapStyleWord( true ); // We also consider 2 rows a sensible default, so that the // JTextArea is always distinguishable from a JTextField textarea.setRows( 2 ); return new JScrollPane( textarea ); } return new JTextField(); } // Characters if ( Character.class.isAssignableFrom( clazz ) ) { return new JTextField(); } // Dates if ( Date.class.equals( clazz ) ) { return new JTextField(); } // Numbers // // Note: we use a text field, not a JSpinner or JSlider, because // BeansBinding gets upset at doing 'setValue( null )' if the Integer // is null. We can still use JSpinner/JSliders for primitives, though. if ( Number.class.isAssignableFrom( clazz ) ) { return new JTextField(); } // Collections if ( Collection.class.isAssignableFrom( clazz ) ) { return new Stub(); } } // Not simple, but don't expand if ( TRUE.equals( attributes.get( DONT_EXPAND ) ) ) { return new JTextField(); } // Nested Metawidget return null; } // // Inner class // /** * Editor for ComboBox whose values use a lookup. */ private static class LookupComboBoxEditor extends BasicComboBoxEditor { // // Private members // private Map mLookups; // // Constructor // public LookupComboBoxEditor( Map lookups ) { if ( lookups == null ) { throw new NullPointerException( "lookups" ); } mLookups = lookups; } // // Public methods // @Override public void setItem( Object item ) { super.setItem( mLookups.get( item ) ); } } /** * Renderer for ComboBox whose values use a lookup. */ private static class LookupComboBoxRenderer extends BasicComboBoxRenderer { // // Private members // private Map mLookups; // // Constructor // public LookupComboBoxRenderer( Map lookups ) { if ( lookups == null ) { throw new NullPointerException( "lookups" ); } mLookups = lookups; } // // Public methods // @Override public Component getListCellRendererComponent( JList list, Object value, int index, boolean selected, boolean hasFocus ) { Component component = super.getListCellRendererComponent( list, value, index, selected, hasFocus ); String lookup = mLookups.get( value ); if ( lookup != null ) { ( (JLabel) component ).setText( lookup ); } return component; } } }