Coverage Report - org.apache.tapestry.form.ListEditMap
 
Classes in this File Line Coverage Branch Coverage Complexity
ListEditMap
0%
0/55
0%
0/20
2
 
 1  
 // Copyright 2004, 2005 The Apache Software Foundation
 2  
 //
 3  
 // Licensed under the Apache License, Version 2.0 (the "License");
 4  
 // you may not use this file except in compliance with the License.
 5  
 // You may obtain a copy of the License at
 6  
 //
 7  
 //     http://www.apache.org/licenses/LICENSE-2.0
 8  
 //
 9  
 // Unless required by applicable law or agreed to in writing, software
 10  
 // distributed under the License is distributed on an "AS IS" BASIS,
 11  
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12  
 // See the License for the specific language governing permissions and
 13  
 // limitations under the License.
 14  
 
 15  
 package org.apache.tapestry.form;
 16  
 
 17  
 import java.util.ArrayList;
 18  
 import java.util.Collections;
 19  
 import java.util.HashMap;
 20  
 import java.util.HashSet;
 21  
 import java.util.List;
 22  
 import java.util.Map;
 23  
 import java.util.Set;
 24  
 
 25  
 import org.apache.tapestry.Tapestry;
 26  
 
 27  
 /**
 28  
  * A utility class often used with the {@link org.apache.tapestry.form.ListEdit} component. A
 29  
  * ListEditMap is loaded with data objects before the ListEdit renders, and again before the
 30  
  * ListEdit rewinds. This streamlines the synchronization of the form against data on the server. It
 31  
  * is most useful when the set of objects is of a manageable size (say, no more than a few hundred
 32  
  * objects).
 33  
  * <p>
 34  
  * The map stores a list of keys, and relates each key to a value. It also tracks a deleted flag for
 35  
  * each key.
 36  
  * <p>
 37  
  * Usage: <br>
 38  
  * The page or component should implement {@link org.apache.tapestry.event.PageBeginRenderListener}
 39  
  * and implement
 40  
  * {@link org.apache.tapestry.event.PageBeginRenderListener#pageBeginRender(org.apache.tapestry.event.PageEvent)}
 41  
  * to initialize the map.
 42  
  * <p>
 43  
  * The external data (from which keys and values are obtained) is queried, and each key/value pair
 44  
  * is {@link #add(Object, Object) added} to the map, in the order that items should be presented.
 45  
  * <p>
 46  
  * The {@link org.apache.tapestry.form.ListEdit}'s source parameter should be bound to the map's
 47  
  * {@link #getKeys() keys} property. The value parameter should be bound to the map's
 48  
  * {@link #setKey(Object) key} property.
 49  
  * <p>
 50  
  * The {@link org.apache.tapestry.form.ListEdit}'s listener parameter should be bound to a listener
 51  
  * method to synchronize a property of the component from the map. <code>
 52  
  *  public void synchronize()
 53  
  *  {
 54  
  *     ListEditMap map = ...;
 55  
  *     <i>Type</i> object = (<i>Type</i>)map.getValue();
 56  
  * 
 57  
  *     if (object == null)
 58  
  *       ...
 59  
  * 
 60  
  *     set<i>Property</i>(object);
 61  
  *  }
 62  
  *  </code>
 63  
  * <p>
 64  
  * You may also connect a {@link org.apache.tapestry.form.Checkbox}'s selected parameter to the
 65  
  * map's {@link #isDeleted() deleted} property.
 66  
  * <p>
 67  
  * You may track inclusion in other sets by subclassing ListEditMap and implementing new boolean
 68  
  * properties. The accessor method should be a call to {@link #checkSet(Set)} and the mutator method
 69  
  * should be a call to {@link #updateSet(Set, boolean)}.
 70  
  * 
 71  
  * @author Howard Lewis Ship
 72  
  * @since 3.0
 73  
  */
 74  
 
 75  0
 public class ListEditMap
 76  
 {
 77  0
     private Map _map = new HashMap();
 78  
 
 79  0
     private List _keys = new ArrayList();
 80  
 
 81  
     private Set _deletedKeys;
 82  
 
 83  
     private Object _currentKey;
 84  
 
 85  
     /**
 86  
      * Records the key and value into this map. The keys may be obtained, in the order in which they
 87  
      * are added, using {@link #getKeys()}. This also sets the current key (so that you may invoke
 88  
      * {@link #setDeleted(boolean)}, for example).
 89  
      */
 90  
 
 91  
     public void add(Object key, Object value)
 92  
     {
 93  0
         _currentKey = key;
 94  
 
 95  0
         _keys.add(_currentKey);
 96  0
         _map.put(_currentKey, value);
 97  0
     }
 98  
 
 99  
     /**
 100  
      * Returns a List of keys, in the order that keys were added to the map (using
 101  
      * {@link #add(Object, Object)}. The caller must not modify the List.
 102  
      */
 103  
 
 104  
     public List getKeys()
 105  
     {
 106  0
         return _keys;
 107  
     }
 108  
 
 109  
     /**
 110  
      * Sets the key for the map. This defines the key used with the other methods:
 111  
      * {@link #getValue()}, {@link #isDeleted()}, {@link #setDeleted(boolean)}.
 112  
      */
 113  
 
 114  
     public void setKey(Object key)
 115  
     {
 116  0
         _currentKey = key;
 117  0
     }
 118  
 
 119  
     /**
 120  
      * Returns the current key within the map.
 121  
      */
 122  
 
 123  
     public Object getKey()
 124  
     {
 125  0
         return _currentKey;
 126  
     }
 127  
 
 128  
     /**
 129  
      * Returns the value for the key (set using {@link #setKey(Object)}). May return null if no
 130  
      * such key has been added (this can occur if a data object is deleted between the time a form
 131  
      * is rendered and the time a form is submitted).
 132  
      */
 133  
 
 134  
     public Object getValue()
 135  
     {
 136  0
         return _map.get(_currentKey);
 137  
     }
 138  
 
 139  
     /**
 140  
      * Returns true if the {@link #setKey(Object) current key} is in the set of deleted keys.
 141  
      */
 142  
 
 143  
     public boolean isDeleted()
 144  
     {
 145  0
         return checkSet(_deletedKeys);
 146  
     }
 147  
 
 148  
     /**
 149  
      * Returns true if the set contains the {@link #getKey() current key}. Returns false is the set
 150  
      * is null, or doesn't contain the current key.
 151  
      */
 152  
 
 153  
     protected boolean checkSet(Set set)
 154  
     {
 155  0
         if (set == null)
 156  0
             return false;
 157  
 
 158  0
         return set.contains(_currentKey);
 159  
     }
 160  
 
 161  
     /**
 162  
      * Adds or removes the {@link #setKey(Object) current key} from the set of deleted keys.
 163  
      */
 164  
 
 165  
     public void setDeleted(boolean value)
 166  
     {
 167  0
         _deletedKeys = updateSet(_deletedKeys, value);
 168  0
     }
 169  
 
 170  
     /**
 171  
      * Updates the set, adding or removing the {@link #getKey() current key} from it. Returns the
 172  
      * set passed in. If the value is true and the set is null, an new instance of {@link HashSet}
 173  
      * is created and returned.
 174  
      */
 175  
 
 176  
     protected Set updateSet(Set set, boolean value)
 177  
     {
 178  0
         Set updatedSet = set;
 179  0
         if (value)
 180  
         {
 181  0
             if (updatedSet == null)
 182  0
                 updatedSet = new HashSet();
 183  
 
 184  0
             updatedSet.add(_currentKey);
 185  
         }
 186  
         else
 187  
         {
 188  0
             if (updatedSet != null)
 189  0
                 updatedSet.remove(_currentKey);
 190  
         }
 191  
 
 192  0
         return updatedSet;
 193  
     }
 194  
 
 195  
     /**
 196  
      * Returns the deleted keys in an unspecified order. Returns a List, which may be empty if no
 197  
      * keys have been deleted.
 198  
      */
 199  
 
 200  
     public List getDeletedKeys()
 201  
     {
 202  0
         return convertSetToList(_deletedKeys);
 203  
     }
 204  
 
 205  
     /**
 206  
      * Removes keys and values that are in the set of deleted keys, then clears the set of deleted
 207  
      * keys. After invoking this method, {@link #getValues()} and {@link #getAllValues()} will
 208  
      * return equivalent lists and {@link #getKeys()} will no longer show any of the deleted keys.
 209  
      * Note that this method <em>does not</em> change the current key. Subclasses that track
 210  
      * additional key sets may want to override this method to remove deleted keys from those key
 211  
      * sets.
 212  
      */
 213  
 
 214  
     public void purgeDeletedKeys()
 215  
     {
 216  0
         if (_deletedKeys == null)
 217  0
             return;
 218  
 
 219  0
         _map.keySet().removeAll(_deletedKeys);
 220  0
         _keys.removeAll(_deletedKeys);
 221  
 
 222  0
         _deletedKeys = null;
 223  0
     }
 224  
 
 225  
     /**
 226  
      * Invoked to convert a set into a List.
 227  
      * 
 228  
      * @param set
 229  
      *            a set (which may be empty or null)
 230  
      * @return a list (possibly empty) of the items in the set
 231  
      */
 232  
 
 233  
     protected List convertSetToList(Set set)
 234  
     {
 235  0
         if (Tapestry.isEmpty(set))
 236  0
             return Collections.EMPTY_LIST;
 237  
 
 238  0
         return new ArrayList(set);
 239  
     }
 240  
 
 241  
     /**
 242  
      * Returns all the values stored in the map, in the order in which values were added to the map
 243  
      * using {@link #add(Object, Object)}.
 244  
      */
 245  
 
 246  
     public List getAllValues()
 247  
     {
 248  0
         int count = _keys.size();
 249  0
         List result = new ArrayList(count);
 250  
 
 251  0
         for (int i = 0; i < count; i++)
 252  
         {
 253  0
             Object key = _keys.get(i);
 254  0
             Object value = _map.get(key);
 255  
 
 256  0
             result.add(value);
 257  
         }
 258  
 
 259  0
         return result;
 260  
     }
 261  
 
 262  
     /**
 263  
      * Returns all the values stored in the map, excluding those whose id has been marked deleted,
 264  
      * in the order in which values were added to the map using {@link #add(Object, Object)}.
 265  
      */
 266  
 
 267  
     public List getValues()
 268  
     {
 269  0
         int deletedCount = Tapestry.size(_deletedKeys);
 270  
 
 271  0
         if (deletedCount == 0)
 272  0
             return getAllValues();
 273  
 
 274  0
         int count = _keys.size();
 275  
 
 276  0
         List result = new ArrayList(count - deletedCount);
 277  
 
 278  0
         for (int i = 0; i < count; i++)
 279  
         {
 280  0
             Object key = _keys.get(i);
 281  
 
 282  0
             if (_deletedKeys.contains(key))
 283  0
                 continue;
 284  
 
 285  0
             Object value = _map.get(key);
 286  0
             result.add(value);
 287  
         }
 288  
 
 289  0
         return result;
 290  
     }
 291  
 
 292  
 }