Coverage Report - org.apache.tapestry5.util.DefaultPrimaryKeyEncoder
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultPrimaryKeyEncoder
98%
47/48
96%
25/26
0
 
 1  
 // Copyright 2007, 2009 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.tapestry5.util;
 16  
 
 17  
 import org.apache.tapestry5.PrimaryKeyEncoder;
 18  
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 19  
 import org.apache.tapestry5.ioc.internal.util.Defense;
 20  
 
 21  
 import java.io.Serializable;
 22  
 import java.util.LinkedHashMap;
 23  
 import java.util.List;
 24  
 import java.util.Map;
 25  
 import java.util.Set;
 26  
 
 27  
 /**
 28  
  * A default, extensible version of {@link org.apache.tapestry5.PrimaryKeyEncoder} that is based on loading known values
 29  
  * into an internal map. When there's a reasonable number (hundreds, perhaps thousands) of items to choose from, and
 30  
  * those items are fast and cheap to read and instantiate, this implementation is a good bet. For very large result
 31  
  * sets, you'll need to create your own implementation of {@link PrimaryKeyEncoder}.
 32  
  *
 33  
  * @param <K> the key type (which must be serializable)
 34  
  * @param <V> the value type
 35  
  * @deprecated See deprecation notes for {@link org.apache.tapestry5.PrimaryKeyEncoder}.
 36  
  */
 37  
 public class DefaultPrimaryKeyEncoder<K extends Serializable, V> implements PrimaryKeyEncoder<K, V>
 38  
 {
 39  40
     private final Map<K, V> keyToValue = new LinkedHashMap<K, V>();
 40  
 
 41  40
     private final Map<V, K> valueToKey = CollectionFactory.newMap();
 42  
 
 43  
     private Set<K> deletedKeys;
 44  
 
 45  
     private K currentKey;
 46  
 
 47  
     private final Class<K> keyType;
 48  
 
 49  
     /**
 50  
      * Compatibility with 5.0: new encoder, key type unknown. You <em>will</em> want to use the other constructor and
 51  
      * specify the key type.
 52  
      */
 53  
     public DefaultPrimaryKeyEncoder()
 54  
     {
 55  2
         this(null);
 56  2
     }
 57  
 
 58  
     /**
 59  
      * @since 5.1.0.0
 60  
      */
 61  
     public DefaultPrimaryKeyEncoder(Class<K> keyType)
 62  40
     {
 63  40
         this.keyType = keyType;
 64  40
     }
 65  
 
 66  
 
 67  
     public Class<K> getKeyType()
 68  
     {
 69  32
         return keyType;
 70  
     }
 71  
 
 72  
     /**
 73  
      * Adds a new key/value pair to the encoder.
 74  
      */
 75  
     public final void add(K key, V value)
 76  
     {
 77  144
         Defense.notNull(key, "key");
 78  144
         Defense.notNull(value, "value");
 79  
 
 80  144
         V existing = keyToValue.get(key);
 81  144
         if (existing != null) throw new IllegalArgumentException(PublicUtilMessages.duplicateKey(key, value, existing));
 82  
 
 83  142
         keyToValue.put(key, value);
 84  
 
 85  
         // TODO: Ensure that the value is unique?
 86  
 
 87  142
         valueToKey.put(value, key);
 88  142
     }
 89  
 
 90  
     /**
 91  
      * Returns the values previously {@link #add(Serializable, Object) added to the encoder}, <em>in the order in which
 92  
      * they were added</em>. Values that are deleted are not returned.
 93  
      *
 94  
      * @return ordered list of values
 95  
      */
 96  
     public final List<V> getValues()
 97  
     {
 98  28
         return valuesNotInKeySet(deletedKeys);
 99  
     }
 100  
 
 101  
     /**
 102  
      * Returns a list of all the values <em>except</em> those values whose keys are in the provided set. The set may be
 103  
      * null, in which case all values are returned.
 104  
      *
 105  
      * @param keySet set of keys identifying values to exclude, or null to exclude no values
 106  
      * @return values (not in the set) in order origionally added
 107  
      */
 108  
     protected final List<V> valuesNotInKeySet(Set<K> keySet)
 109  
     {
 110  28
         if (keySet == null || keySet.isEmpty()) return getAllValues();
 111  
 
 112  2
         List<V> result = CollectionFactory.newList();
 113  
 
 114  2
         for (Map.Entry<K, V> entry : keyToValue.entrySet())
 115  
         {
 116  
 
 117  6
             if (keySet.contains(entry.getKey())) continue;
 118  
 
 119  4
             result.add(entry.getValue());
 120  
         }
 121  
 
 122  2
         return result;
 123  
     }
 124  
 
 125  
     public final List<V> getAllValues()
 126  
     {
 127  32
         List<V> result = CollectionFactory.newList();
 128  
 
 129  32
         for (Map.Entry<K, V> entry : keyToValue.entrySet())
 130  
         {
 131  130
             result.add(entry.getValue());
 132  
         }
 133  
 
 134  32
         return result;
 135  
     }
 136  
 
 137  
     /**
 138  
      * For a previously {@link #add(Serializable, Object) added key/value pair}, returns the key corresponding to the
 139  
      * given value.
 140  
      */
 141  
     public final K toKey(V value)
 142  
     {
 143  74
         Defense.notNull(value, "value");
 144  
 
 145  74
         currentKey = valueToKey.get(value);
 146  
 
 147  74
         if (currentKey == null) throw new IllegalArgumentException(PublicUtilMessages.missingValue(value, valueToKey
 148  
                 .keySet()));
 149  
 
 150  72
         return currentKey;
 151  
     }
 152  
 
 153  
     public final V toValue(K key)
 154  
     {
 155  34
         V result = keyToValue.get(key);
 156  
 
 157  34
         if (result == null)
 158  
         {
 159  4
             result = provideMissingObject(key);
 160  
 
 161  4
             currentKey = key;
 162  
         }
 163  
         else
 164  
         {
 165  30
             currentKey = key;
 166  
         }
 167  
 
 168  34
         return result;
 169  
     }
 170  
 
 171  
     /**
 172  
      * Invoked by {@link #toValue(Serializable)} whenever a key can not be converted to a value using the internal
 173  
      * cache. This is an opportunity to record the fact that an error occured (they key was not valuable, possibly
 174  
      * because it points to a deleted entity object) and provide a temporary object. This method may return null, but in
 175  
      * a typical application, that will likely case NullPointerExceptions further down the processing chain.
 176  
      * <p/>
 177  
      * This implementation returns null, and is intended to be overriden in subclasses.
 178  
      *
 179  
      * @param key key for which a value is required
 180  
      * @return a substitute value, or null
 181  
      */
 182  
     protected V provideMissingObject(K key)
 183  
     {
 184  2
         return null;
 185  
     }
 186  
 
 187  
     public final boolean isDeleted()
 188  
     {
 189  4
         return inKeySet(deletedKeys);
 190  
     }
 191  
 
 192  
     public final void setDeleted(boolean value)
 193  
     {
 194  8
         deletedKeys = modifyKeySet(deletedKeys, value);
 195  8
     }
 196  
 
 197  
     /**
 198  
      * Returns true if the current key is in the provided set.
 199  
      *
 200  
      * @param keySet the set of keys to check, or null
 201  
      * @return true if the key is in the set, false if it is missing (or if keySet is null)
 202  
      */
 203  
     protected final boolean inKeySet(Set<K> keySet)
 204  
     {
 205  4
         return keySet != null && keySet.contains(currentKey);
 206  
     }
 207  
 
 208  
     /**
 209  
      * Modifies a keySet to add or remove the current key. If necessary, a new Set is created.
 210  
      * <p/>
 211  
      * Useage: <code> private Set<K> myFlagKeys;
 212  
      * <p/>
 213  
      * public boolean void setMyFlag(boolean value) { myFlagKeys = modifySet(myFlagKeys, value); } </code>
 214  
      *
 215  
      * @param keySet the set of keys, or null
 216  
      * @param value  true to add the current key, false to remove
 217  
      * @return the provided key set, or a new one
 218  
      */
 219  
     protected final Set<K> modifyKeySet(Set<K> keySet, boolean value)
 220  
     {
 221  8
         if (keySet == null)
 222  
         {
 223  6
             if (!value) return null;
 224  
 
 225  4
             keySet = CollectionFactory.newSet();
 226  
         }
 227  
 
 228  6
         if (value) keySet.add(currentKey);
 229  2
         else keySet.remove(currentKey);
 230  
 
 231  6
         return keySet;
 232  
     }
 233  
 
 234  
     /**
 235  
      * Does nothing. Subclasses may override as necessary.
 236  
      */
 237  
     public void prepareForKeys(List<K> keys)
 238  
     {
 239  0
     }
 240  
 }