Coverage Report - org.kuali.rice.core.api.mo.common.Attributes
 
Classes in this File Line Coverage Branch Coverage Complexity
Attributes
61%
43/70
59%
19/32
2.4
Attributes$1
N/A
N/A
2.4
Attributes$Adapter
84%
11/13
66%
8/12
2.4
Attributes$Constants
50%
1/2
N/A
2.4
 
 1  
 package org.kuali.rice.core.api.mo.common;
 2  
 
 3  
 import org.apache.commons.lang.builder.EqualsBuilder;
 4  
 import org.apache.commons.lang.builder.HashCodeBuilder;
 5  
 import org.apache.commons.lang.builder.ToStringBuilder;
 6  
 import org.kuali.rice.core.util.ConcreteKeyValue;
 7  
 import org.kuali.rice.core.util.KeyValue;
 8  
 import org.kuali.rice.core.util.jaxb.StringMapEntry;
 9  
 import org.kuali.rice.core.util.jaxb.StringMapEntryList;
 10  
 
 11  
 import javax.xml.bind.annotation.XmlRootElement;
 12  
 import javax.xml.bind.annotation.adapters.XmlAdapter;
 13  
 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 14  
 import java.io.IOException;
 15  
 import java.io.ObjectInputStream;
 16  
 import java.io.Serializable;
 17  
 import java.util.AbstractMap;
 18  
 import java.util.ArrayList;
 19  
 import java.util.Collection;
 20  
 import java.util.Collections;
 21  
 import java.util.HashMap;
 22  
 import java.util.HashSet;
 23  
 import java.util.List;
 24  
 import java.util.Map;
 25  
 import java.util.Set;
 26  
 
 27  
 /**
 28  
  * This is a generic attributes class in rice.  It is essentially a list of key-value
 29  
  * pairs where the key & value are strings & the keys are unique.
 30  
  */
 31  
 @XmlRootElement
 32  
 @XmlJavaTypeAdapter(Attributes.Adapter.class)
 33  5
 public final class Attributes implements Serializable {
 34  
 
 35  
     private static final long serialVersionUID = -2804341886674598357L;
 36  
 
 37  1
         private static final Attributes EMPTY = new Attributes(Collections.<String, String>emptyMap());
 38  
 
 39  
     private final Map<String, String> keyValues;
 40  
 
 41  
     //should only be reassigned on deserialization, can't be final b/c of deserialization hook
 42  14
     private transient Object lock = new Object();
 43  
     private transient Set<Map.Entry<String, String>> entrySetCache;
 44  
 
 45  
     /**
 46  
      * This constructor should never be called except during JAXB unmarshalling.
 47  
      */
 48  
     @SuppressWarnings("unused")
 49  0
     private Attributes() {
 50  0
         this.keyValues = null;
 51  0
     }
 52  
 
 53  14
     private Attributes(final Map<String, String> map) {
 54  14
         this.keyValues = new HashMap<String, String>(map);
 55  14
     }
 56  
 
 57  
     /**
 58  
      * Creates empty attributes.
 59  
      *
 60  
      * @return Attributes
 61  
      */
 62  
     public static Attributes empty() {
 63  10
         return EMPTY;
 64  
     }
 65  
 
 66  
     /**
 67  
      * Creates attributes from a {@link Map}.  Map cannot be null.
 68  
      *
 69  
      * @return Attributes
 70  
      * @throws IllegalArgumentException if map is null
 71  
      */
 72  
     public static Attributes fromMap(Map<String, String> map) {
 73  13
         if (map == null) {
 74  1
             return empty();
 75  
         }
 76  
 
 77  12
         if (map.isEmpty()) {
 78  1
             return empty();
 79  
         }
 80  
 
 81  11
         return new Attributes(map);
 82  
     }
 83  
 
 84  
     /**
 85  
      * Creates attributes from a {@link Map.Entry}.  Map.Entry cannot be null.
 86  
      *
 87  
      * @return Attributes
 88  
      * @throws IllegalArgumentException if entry is null or entry.key is null
 89  
      */
 90  
     public static Attributes fromMapEntry(Map.Entry<String, String> entry) {
 91  3
         if (entry == null) {
 92  1
             return empty();
 93  
         }
 94  
 
 95  2
         if (entry.getKey() == null) {
 96  1
             throw new IllegalArgumentException("entry.key is null");
 97  
         }
 98  
 
 99  1
         return fromMap(Collections.singletonMap(entry.getKey(), entry.getValue()));
 100  
     }
 101  
 
 102  
     /**
 103  
      * Creates attributes from strings.  Key cannot be null. Value can be null
 104  
      *
 105  
      * @return Attributes
 106  
      * @throws IllegalArgumentException if key is null
 107  
      */
 108  
     public static Attributes fromStrings(String key, String value) {
 109  2
         if (key == null) {
 110  1
             throw new IllegalArgumentException("key is null");
 111  
         }
 112  
 
 113  1
         return fromMap(Collections.singletonMap(key, value));
 114  
     }
 115  
 
 116  
     /**
 117  
      * Creates attributes from a {@link KeyValue}.  KeyValue cannot be null.
 118  
      *
 119  
      * @return Attributes
 120  
      * @throws IllegalArgumentException if keyValue is null
 121  
      */
 122  
     public static Attributes fromKeyValue(KeyValue keyValue) {
 123  3
         if (keyValue == null) {
 124  1
             return empty();
 125  
         }
 126  
 
 127  2
         if (keyValue.getKey() == null) {
 128  1
             throw new IllegalArgumentException("keyValue.key is null");
 129  
         }
 130  
 
 131  1
         return fromMap(Collections.singletonMap(keyValue.getKey(), keyValue.getValue()));
 132  
     }
 133  
 
 134  
     /**
 135  
      * Creates attributes from a Collection of {@link KeyValue}.  KeyValues cannot be null.
 136  
      *
 137  
      * @return Attributes
 138  
      * @throws IllegalArgumentException if keyValues is null
 139  
      */
 140  
     public static Attributes fromKeyValues(Collection<? extends KeyValue> keyValues) {
 141  0
         if (keyValues == null) {
 142  0
             return empty();
 143  
         }
 144  0
         final Map<String, String> pairs = new HashMap<String, String>();
 145  0
         for (KeyValue keyValue : keyValues) {
 146  0
             if (keyValue == null) {
 147  0
                  throw new IllegalArgumentException("keyValue is null");
 148  
             }
 149  
 
 150  0
             if (keyValue.getKey() == null) {
 151  0
                 throw new IllegalArgumentException("keyValue.key is null");
 152  
             }
 153  
 
 154  0
             pairs.put(keyValue.getKey(), keyValue.getValue());
 155  
         }
 156  0
         return fromMap(pairs);
 157  
     }
 158  
 
 159  
     /**
 160  
      * Converts attributes to a mutable {@link Map}.
 161  
      * The map returned is disconnected from this Attributes class and is mutable.
 162  
      *
 163  
      * @return a Map
 164  
      */
 165  
     public Map<String, String> toMap() {
 166  2
         return new HashMap<String, String>(keyValues);
 167  
     }
 168  
 
 169  
     /**
 170  
      * Converts attributes to a mutable list of immutable {@link KeyValue} objects.
 171  
      * The list returned is disconnected from this Attributes class and is mutable.
 172  
      */
 173  
     public List<KeyValue> toKeyValues() {
 174  0
         final List<KeyValue> kv = new ArrayList<KeyValue>();
 175  0
         for (Map.Entry<String, String> entry : keyValues.entrySet()) {
 176  0
             kv.add(new ConcreteKeyValue(entry));
 177  
         }
 178  0
         return kv;
 179  
     }
 180  
     //map-like methods
 181  
 
 182  
     /**
 183  
      * Returns the amount of attributes in this data structure.
 184  
      *
 185  
      * @return the size
 186  
      */
 187  
     public int size() {
 188  0
         return keyValues.size();
 189  
     }
 190  
 
 191  
     /**
 192  
      * Whether this data structure does not contain any attributes.
 193  
      *
 194  
      * @return true if empty false if not
 195  
      */
 196  
     public boolean isEmpty() {
 197  0
         return keyValues.isEmpty();
 198  
     }
 199  
 
 200  
     /**
 201  
      * Whether this data structure contains an attribute with a given key. Key may not be null.
 202  
      *
 203  
      * @param key the key
 204  
      * @return true if the key exists false if not
 205  
      * @throws IllegalArgumentException if the key is null
 206  
      */
 207  
     public boolean containsKey(String key) {
 208  1
         validateKey(key);
 209  1
         return keyValues.containsKey(key);
 210  
     }
 211  
 
 212  
     /**
 213  
      * Whether this data structure contains an attribute with a given value. Value may be null.
 214  
      *
 215  
      * @param value the value
 216  
      * @return true if the value exists false if not
 217  
      */
 218  
     public boolean containsValue(String value) {
 219  0
         return keyValues.containsValue(value);
 220  
     }
 221  
 
 222  
     /**
 223  
      * Gets an attribute value from a key. Key may not be null.
 224  
      *
 225  
      * @param key the key
 226  
      * @return true if the value exists false if not
 227  
      */
 228  
     public String get(String key) {
 229  0
         validateKey(key);
 230  0
         return keyValues.get(key);
 231  
     }
 232  
 
 233  
     /**
 234  
      * Gets a mutable {@link Set} of all attribute keys.
 235  
      *
 236  
      * @return the set.
 237  
      */
 238  
     public Set<String> keySet() {
 239  0
         return keyValues.keySet();
 240  
     }
 241  
 
 242  
     /**
 243  
      * Gets a mutable {@link Collection} of all attribute values.  Could contain null values.
 244  
      *
 245  
      * @return the collection.
 246  
      */
 247  
     public Collection<String> values() {
 248  0
         return keyValues.values();
 249  
     }
 250  
 
 251  
     /**
 252  
      * Gets a immutable {@link Set} of immutable {@link Map.Entry} instances.
 253  
      *
 254  
      * @return the set.
 255  
      */
 256  
     public Set<Map.Entry<String, String>> entrySet() {
 257  
         //not sure if we really need caching here - but adding it
 258  
         //b/c it is easy enough to implement
 259  6
         synchronized (lock) {
 260  6
             if (entrySetCache == null) {
 261  6
                 final Set<Map.Entry<String, String>> temp = new HashSet<Map.Entry<String, String>>();
 262  6
                 for (Map.Entry<String, String> e : keyValues.entrySet()) {
 263  6
                     if (e != null) {
 264  6
                         temp.add(new AbstractMap.SimpleImmutableEntry<String, String>(e.getKey(), e.getValue()));
 265  
                     }
 266  
                 }
 267  6
                 entrySetCache = Collections.unmodifiableSet(temp);
 268  
             }
 269  6
         }
 270  6
         return entrySetCache;
 271  
     }
 272  
 
 273  
     private static void validateKey(String key) {
 274  1
         if (key == null) {
 275  0
             throw new IllegalArgumentException("key is null");
 276  
         }
 277  1
     }
 278  
 
 279  
     @Override
 280  
     public int hashCode() {
 281  0
         return HashCodeBuilder.reflectionHashCode(this, Constants.HASH_CODE_EQUALS_EXCLUDE);
 282  
     }
 283  
 
 284  
     @Override
 285  
     public boolean equals(Object obj) {
 286  6
         return EqualsBuilder.reflectionEquals(obj, this, Constants.HASH_CODE_EQUALS_EXCLUDE);
 287  
     }
 288  
 
 289  
     @Override
 290  
     public String toString() {
 291  0
         return ToStringBuilder.reflectionToString(this);
 292  
     }
 293  
 
 294  
     //resetting transient fields on deserialization
 295  
     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
 296  1
         in.defaultReadObject();
 297  1
         lock = new Object();
 298  1
     }
 299  
 
 300  
     /**
 301  
      * Defines some internal constants used on this class.
 302  
      */
 303  0
     static class Constants {
 304  1
         final static String[] HASH_CODE_EQUALS_EXCLUDE = {"entrySetCache", "lock"};
 305  
     }
 306  
     
 307  5
     public static class Adapter extends XmlAdapter<StringMapEntryList, Attributes> {
 308  
 
 309  
             @Override
 310  
             public StringMapEntryList marshal(Attributes attributes) throws Exception {
 311  1
                     if (attributes == null || attributes.keyValues == null) {
 312  0
                             return null;
 313  
                     }
 314  1
                     List<StringMapEntry> entries = new ArrayList<StringMapEntry>(attributes.keyValues.size());
 315  1
                     for (Map.Entry<String, String> entry : attributes.keyValues.entrySet()) {
 316  1
                             entries.add(new StringMapEntry(entry));
 317  
                     }
 318  1
                     return new StringMapEntryList(entries);
 319  
             }
 320  
 
 321  
             @Override
 322  
             public Attributes unmarshal(StringMapEntryList entries) throws Exception {
 323  2
                     if (entries == null || entries.getEntries() == null) {
 324  0
                             return null;
 325  
                     }
 326  2
                     Map<String, String> resultMap = new HashMap<String, String>();
 327  2
                     for (StringMapEntry entry : entries.getEntries()) {
 328  2
                             resultMap.put(entry.getKey(), entry.getValue());
 329  
                     }
 330  2
                     return new Attributes(resultMap);
 331  
             }
 332  
 
 333  
     }
 334  
 
 335  
 }