001    package com.sptci.echo2;
002    
003    import java.io.Serializable;
004    
005    import java.lang.reflect.Field;
006    import java.lang.reflect.InvocationTargetException;
007    import java.lang.reflect.Method;
008    
009    import java.util.ArrayList;
010    import java.util.HashMap;
011    import java.util.HashSet;
012    import java.util.Iterator;
013    import java.util.List;
014    import java.util.Map;
015    import java.util.TreeMap;
016    import java.util.logging.Logger;
017    
018    import nextapp.echo2.app.ListBox;
019    import nextapp.echo2.app.SelectField;
020    import nextapp.echo2.app.button.ToggleButton;
021    import nextapp.echo2.app.list.ListModel;
022    import nextapp.echo2.app.text.TextComponent;
023    
024    import com.sptci.ReflectionUtility;
025    
026    /**
027     * An abstract <code>updater</code> used to update UI containers or
028     * JavaBean objects from one another.
029     *
030     * <p>Copyright 2006 Sans Pareil Technologies, Inc.</p>
031     * @author Rakesh Vidyadharan 2006-02-07
032     * @version $Id: Updater.java,v 1.3 2006/02/09 21:27:31 rakesh Exp $
033     */
034    public abstract class Updater implements Serializable
035    {
036      /**
037       * The logger used to log errors/warnings to.
038       */
039      private static transient final Logger logger =
040        Logger.getLogger( "com.sptci.echo2.Updater" );
041    
042      /**
043       * The UI container that is to be updated.
044       */
045      protected Object uiContainer;
046    
047      /**
048       * The java bean that contains the data to use to update the
049       * {@link #uiContainer}.
050       */
051      protected Object bean;
052    
053      /**
054       * Default constructor.  Cannot be instantiated.
055       */
056      protected Updater() {}
057    
058      /**
059       * Create a new instance with the specified UI container and java 
060       * bean.
061       *
062       * @param uiContainer The {@link #uiContainer} to use.
063       * @param bean The {@link #bean} to use.
064       */
065      protected Updater( Object uiContainer, Object bean )
066      {
067        setUiContainer( uiContainer );
068        setBean( bean );
069      }
070    
071      /**
072       * Fetch the values of all the fields in {@link #bean} that have
073       * an <code>accessor</code> method conforming to JavaBean naming
074       * convention.
075       *
076       * @see ReflectionUtility#fetchFields
077       * @see ReflectionUtility#fetchMethod( Object, String, Class[] )
078       * @return Map A map with the field name as <code>key</code> and
079       *   the field value as <code>value</code>.
080       * @throws IllegalAccessException If a custom security policy
081       *   restricts access to the bean fields.
082       * @throws InvocationTargetException If the accessor method for
083       *   the field could not be invoked successfully.
084       */
085      protected Map<String, Object> beanValues() 
086        throws IllegalAccessException, InvocationTargetException
087      {
088        Map<String, Object> values = new HashMap<String, Object>();
089        for ( Map.Entry<String, Field> entry : 
090            ReflectionUtility.fetchFields( bean ).entrySet() )
091        {
092          Field field = entry.getValue();
093          String methodName = "get" + 
094            field.getName().substring( 0, 1 ).toUpperCase() + 
095            field.getName().substring( 1 );
096          Method method = ReflectionUtility.fetchMethod( bean,
097              methodName, new Class[0] );
098          if ( method != null )
099          {
100            Object value = method.invoke( bean );
101            values.put( field.getName(), value );
102          }
103          else
104          {
105            logger.fine( "No accessor method in bean: " + bean.getClass() +
106                " for field: " + field.getName() );
107          }
108        }
109    
110        return values;
111      }
112    
113      /**
114       * Fetch the values of all the fields in {@link #uiContainer}.
115       * Convert the values of the UI components to the mapping scheme
116       * used in bean generation.
117       *
118       * @see ReflectionUtility#fetchFields
119       * @see ReflectionUtility#fetchMethod( Object, String, Class[] )
120       * @return Map A map with the field name as <code>key</code> and
121       *   the field value as <code>value</code>.
122       * @throws IllegalAccessException If a custom security policy
123       *   restricts access to the bean fields.
124       * @throws InvocationTargetException If the accessor method for
125       *   the field could not be invoked successfully.
126       */
127      protected Map<String, Object> uiValues() 
128        throws IllegalAccessException, InvocationTargetException
129      {
130        Map<String, Object> values = new HashMap<String, Object>();
131    
132        for ( Map.Entry<String, Field> entry : 
133            ReflectionUtility.fetchFields( uiContainer ).entrySet() )
134        {
135          Field field = entry.getValue();
136          Object object = field.get( uiContainer );
137          Object value = null;
138    
139          if ( object instanceof nextapp.echo2.app.text.TextComponent )
140          {
141            value = ( (TextComponent) object ).getText();
142          }
143          else if ( object instanceof nextapp.echo2.app.ListBox )
144          {
145            List<ListItem> list = new ArrayList<ListItem>();
146            ListBox box = (ListBox) object;
147            ListModel model = box.getModel();
148            int[] selected = box.getSelectedIndices();
149            HashSet<Integer> set = new HashSet<Integer>( selected.length );
150            for ( int i : selected )
151            {
152              set.add( i );
153            }
154    
155            for ( int i = 0; i < model.size(); ++i )
156            {
157              if ( set.contains( i ) )
158              {
159                list.add( new ListItem( (String) model.get( i ), true ) );
160              }
161              else
162              {
163                list.add( new ListItem( (String) model.get( i ), false ) );
164              }
165            }
166    
167            value = list;
168          }
169          else if ( object instanceof nextapp.echo2.app.SelectField )
170          {
171            List<ListItem> list = new ArrayList<ListItem>();
172            SelectField select = (SelectField) object;
173            ListModel model = select.getModel();
174            int index = select.getSelectedIndex();
175    
176            for ( int i = 0; i < model.size(); ++i )
177            {
178              if ( i == index )
179              {
180                list.add( new ListItem( (String) model.get( i ), true ) );
181              }
182              else
183              {
184                list.add( new ListItem( (String) model.get( i ), false ) );
185              }
186            }
187    
188            value = list;
189          }
190          else if ( object instanceof java.util.Map )
191          {
192            Map<String, Boolean> map = new TreeMap<String, Boolean>();
193    
194            for ( Iterator iterator = ( (Map) object ).entrySet().iterator();
195                iterator.hasNext(); )
196            {
197              Map.Entry mapEntry = (Map.Entry) iterator.next();
198              if ( mapEntry.getValue() instanceof 
199                  nextapp.echo2.app.button.ToggleButton )
200              {
201                ToggleButton button = (ToggleButton) mapEntry.getValue();
202                String key = (String) mapEntry.getKey();
203                int index = 0;
204                if ( ( index = key.lastIndexOf( ">" ) ) != -1 )
205                {
206                  key = key.substring( index + 1 );
207                }
208    
209                map.put( key, button.isSelected() );
210              }
211            }
212    
213            value = map;
214          }
215    
216          values.put( entry.getKey(), value );
217        }
218    
219        return values;
220      }
221    
222      /**
223       * Update the fields of the desired component with the data contained
224       * in similarly named fields in the other component.
225       */
226      public abstract void update();
227      
228      /**
229       * Returns {@link #uiContainer}.
230       *
231       * @return Object The value/reference of/to uiContainer.
232       */
233      public final Object getUiContainer()
234      {
235        return uiContainer;
236      }
237      
238      /**
239       * Set {@link #uiContainer}.
240       *
241       * @param uiContainer The value to set.
242       */
243      public final void setUiContainer( Object uiContainer )
244      {
245        this.uiContainer = uiContainer;
246      }
247      
248      /**
249       * Returns {@link #bean}.
250       *
251       * @return Object The value/reference of/to bean.
252       */
253      public final Object getBean()
254      {
255        return bean;
256      }
257      
258      /**
259       * Set {@link #bean}.
260       *
261       * @param bean The value to set.
262       */
263      public final void setBean( Object bean )
264      {
265        this.bean = bean;
266      }
267    }