001 package com.sptci.echo2;
002
003 import java.beans.IntrospectionException;
004 import java.beans.PropertyChangeEvent;
005 import java.beans.PropertyDescriptor;
006
007 import java.lang.reflect.Field;
008 import java.lang.reflect.Method;
009 import java.lang.reflect.Modifier;
010
011 import java.util.List;
012 import java.util.Map;
013 import java.util.logging.Logger;
014
015 import nextapp.echo2.app.ListBox;
016 import nextapp.echo2.app.SelectField;
017 import nextapp.echo2.app.button.ToggleButton;
018
019 import com.sptci.ReflectionUtility;
020
021 /**
022 * An implementation of <code>PropertyChangeListener</code> used
023 * to synchronise changes made to properties in UI components or the
024 * {@link #bean} java bean with each other.
025 *
026 * <p>Copyright 2006 Sans Pareil Technologies, Inc.</p>
027 * @author Rakesh Vidyadharan 2006-01-22
028 * @version $Id: PropertyChangeListener.java,v 1.4 2006/02/09 16:16:02 rakesh Exp $
029 */
030 public abstract class PropertyChangeListener
031 extends com.sptci.PropertyChangeListener
032 {
033 /**
034 * The logger used to log errors to.
035 */
036 private static final Logger logger =
037 Logger.getLogger( "com.sptci.echo2.PropertyChangeListener" );
038
039 /**
040 * Default constructor. Just invokes super class constructor.
041 */
042 protected PropertyChangeListener()
043 {
044 super();
045 }
046
047 /**
048 * Create a new instance of the class using the specified java bean.
049 *
050 * @param bean The data object which is to be managed.
051 * @throws IntrospectionException If errors are encountered while
052 * introspecting the {@link #bean} class.
053 */
054 public PropertyChangeListener( Object bean )
055 throws IntrospectionException
056 {
057 super( bean );
058 }
059
060 /**
061 * Over-ridden to handle special events generated by the Echo2
062 * framework. Events that require special handling are usually
063 * <code>CheckBox</code>, <code>AbstractList</code> and
064 * <code>RadioButton</code> components. These components must
065 * be modelled as <code>Map</code> and <code>List</code> fields in
066 * the {@link #bean}.
067 *
068 * @see ReflectionUtility#fetchObject
069 * @see #processMap
070 * @see #processList
071 * @param event A <code>PropertyChangeEvent</code> object that
072 * contains the property to change and the old and new values.
073 * @param descriptor A <code>PropertyDescriptor</code> object that
074 * describes the property
075 * @throws BindingException If errors are encountered while trying
076 * to invoke the appropriate method.
077 */
078 protected void modifyObject( PropertyChangeEvent event,
079 PropertyDescriptor descriptor ) throws BindingException
080 {
081 try
082 {
083 Object object =
084 ReflectionUtility.fetchObject( descriptor.getName(), bean );
085
086 if ( object instanceof java.util.Map )
087 {
088 processMap( (Map) object, event, descriptor );
089 }
090 else if ( object instanceof java.util.List )
091 {
092 processList( (List) object, event, descriptor );
093 }
094 else
095 {
096 super.modifyObject( event, descriptor );
097 }
098 }
099 catch ( Throwable t1 )
100 {
101 throw new BindingException( t1 );
102 }
103 }
104
105 /**
106 * Process a field with type <code>Map</code> in {@link #bean}.
107 * Adds an entry to the map using the name and value in the
108 * event.
109 *
110 * @param map The map that is to be updated.
111 * @param event A <code>PropertyChangeEvent</code> object that
112 * contains the property to change and the old and new values.
113 * @param descriptor A <code>PropertyDescriptor</code> object that
114 * describes the property
115 */
116 protected void processMap( Map map, PropertyChangeEvent event,
117 PropertyDescriptor descriptor )
118 {
119 if ( event.getSource() instanceof
120 nextapp.echo2.app.button.ToggleButton )
121 {
122 ToggleButton button = (ToggleButton) event.getSource();
123 String name = button.getActionCommand();
124
125 if ( name != null && name.indexOf( ">" ) != -1 )
126 {
127 name = name.substring( name.indexOf( ">" ) + 1 );
128 }
129 map.put( name, event.getNewValue() );
130 }
131 else
132 {
133 logger.warning( "Map error name: " +
134 descriptor.getName() + " object: " + map +
135 " source: " + event.getSource() );
136 }
137 }
138
139 /**
140 * Process a field with type <code>List</code> in {@link #bean}.
141 * Sets the value of the list entry at the appropriate index using
142 * the values in the event.
143 *
144 * @param list The list that is to be updated.
145 * @param event A <code>PropertyChangeEvent</code> object that
146 * contains the property to change and the old and new values.
147 * @param descriptor A <code>PropertyDescriptor</code> object that
148 * describes the property
149 */
150 protected void processList( List list, PropertyChangeEvent event,
151 PropertyDescriptor descriptor )
152 {
153 if ( event.getSource() instanceof
154 nextapp.echo2.app.ListBox )
155 {
156 ListBox listBox = (ListBox) event.getSource();
157 for ( int index : listBox.getSelectedIndices() )
158 {
159 String value = (String) listBox.getModel().get( index );
160 list.set( index, new ListItem( value, true ) );
161 }
162 }
163 else if ( event.getSource() instanceof
164 nextapp.echo2.app.SelectField )
165 {
166 SelectField selectField = (SelectField) event.getSource();
167 if ( selectField.getSelectedIndex() != -1 )
168 {
169 list.set( selectField.getSelectedIndex(),
170 new ListItem( (String) selectField.getSelectedItem(),
171 true ) );
172 }
173 }
174 else
175 {
176 logger.warning( "List error name: " +
177 descriptor.getName() + " object: " + list +
178 " source: " + event.getSource() );
179 }
180 }
181
182 /**
183 * Parse the field name specified, and remove any complex components
184 * used to set the name as <code>actionCommands</code>.
185 *
186 * @param name The name that is to be parsed.
187 * @return String The parsed and corrected (if necessary) name.
188 */
189 protected String parseName( String name )
190 {
191 if ( name != null && name.indexOf( ">" ) != -1 )
192 {
193 name = name.substring( 0, name.indexOf( ">" ) );
194 }
195
196 return name;
197 }
198 }