Home | History | Annotate | Download | only in item
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.loganalysis.item;
     17 
     18 import org.json.JSONException;
     19 import org.json.JSONObject;
     20 
     21 import java.util.HashMap;
     22 import java.util.HashSet;
     23 import java.util.Map;
     24 import java.util.Set;
     25 
     26 /**
     27  * An implementation of the {@link IItem} interface which implements helper methods.
     28  */
     29 public class GenericItem implements IItem {
     30     private Map<String, Object> mAttributes = new HashMap<String, Object>();
     31     private Set<String> mAllowedAttributes;
     32 
     33     protected GenericItem(Set<String> allowedAttributes) {
     34         mAllowedAttributes = new HashSet<String>();
     35         mAllowedAttributes.addAll(allowedAttributes);
     36     }
     37 
     38     protected GenericItem(Set<String> allowedAttributes, Map<String, Object> attributes) {
     39         this(allowedAttributes);
     40 
     41         for (Map.Entry<String, Object> entry : attributes.entrySet()) {
     42             setAttribute(entry.getKey(), entry.getValue());
     43         }
     44     }
     45 
     46     /**
     47      * {@inheritDoc}
     48      */
     49     @Override
     50     public IItem merge(IItem other) throws ConflictingItemException {
     51         if (this == other) {
     52             return this;
     53         }
     54         if (other == null || getClass() != other.getClass()) {
     55             throw new ConflictingItemException("Conflicting class types");
     56         }
     57 
     58         return new GenericItem(mAllowedAttributes, mergeAttributes(other, mAllowedAttributes));
     59     }
     60 
     61     /**
     62      * Merges the attributes from the item and another and returns a Map of the merged attributes.
     63      * <p>
     64      * Goes through each field in the item preferring non-null attributes over null attributes.
     65      * </p>
     66      *
     67      * @param other The other item
     68      * @return A Map from Strings to Objects containing merged attributes.
     69      * @throws ConflictingItemException If the two items are not consistent.
     70      */
     71     protected Map<String, Object> mergeAttributes(IItem other, Set<String> attributes)
     72             throws ConflictingItemException {
     73         if (this == other) {
     74             return mAttributes;
     75         }
     76         if (other == null || getClass() != other.getClass()) {
     77             throw new ConflictingItemException("Conflicting class types");
     78         }
     79 
     80         GenericItem item = (GenericItem) other;
     81         Map<String, Object> mergedAttributes = new HashMap<String, Object>();
     82         for (String attribute : attributes) {
     83             mergedAttributes.put(attribute,
     84                     mergeObjects(getAttribute(attribute), item.getAttribute(attribute)));
     85         }
     86         return mergedAttributes;
     87     }
     88 
     89     /** {@inheritDoc} */
     90     @Override
     91     public boolean isConsistent(IItem other) {
     92         if (this == other) {
     93             return true;
     94         }
     95         if (other == null || getClass() != other.getClass()) {
     96             return false;
     97         }
     98 
     99         GenericItem item = (GenericItem) other;
    100         for (String attribute : mAllowedAttributes) {
    101             if (!areConsistent(getAttribute(attribute), item.getAttribute(attribute))) {
    102                 return false;
    103             }
    104         }
    105         return true;
    106     }
    107 
    108     /**
    109      * {@inheritDoc}
    110      */
    111     @Override
    112     public boolean equals(Object other) {
    113         if (this == other) {
    114             return true;
    115         }
    116         if (other == null || getClass() != other.getClass()) {
    117             return false;
    118         }
    119 
    120         GenericItem item = (GenericItem) other;
    121         for (String attribute : mAllowedAttributes) {
    122             if (!areEqual(getAttribute(attribute), item.getAttribute(attribute))) {
    123                 return false;
    124             }
    125         }
    126         return true;
    127     }
    128 
    129     /** {@inheritDoc} */
    130     @Override
    131     public int hashCode() {
    132         int result = 13;
    133         for (String attribute : mAllowedAttributes) {
    134             Object val = getAttribute(attribute);
    135             result += 37 * (val == null ? 0 : val.hashCode());
    136         }
    137         return result;
    138     }
    139 
    140     /**
    141      * {@inheritDoc}
    142      * <p>
    143      * This method will only return a JSON object with attributes that the JSON library knows how to
    144      * encode, along with attributes which implement the {@link IItem} interface.  If objects are
    145      * stored in this class that fall outside this category, they must be encoded outside of this
    146      * method.
    147      * </p>
    148      */
    149     @Override
    150     public JSONObject toJson() {
    151         JSONObject object = new JSONObject();
    152         for (Map.Entry<String, Object> entry : mAttributes.entrySet()) {
    153             final String key = entry.getKey();
    154             final Object attribute = entry.getValue();
    155             try {
    156                 if (attribute != null && attribute instanceof IItem) {
    157                     object.put(key, ((IItem) attribute).toJson());
    158                 } else {
    159                     object.put(key, attribute);
    160                 }
    161             } catch (JSONException e) {
    162                 // Ignore
    163             }
    164         }
    165         return object;
    166     }
    167 
    168     /**
    169      * Set an attribute to a value.
    170      *
    171      * @param attribute The name of the attribute.
    172      * @param value The value.
    173      * @throws IllegalArgumentException If the attribute is not in allowedAttributes.
    174      */
    175     protected void setAttribute(String attribute, Object value) throws IllegalArgumentException {
    176         if (!mAllowedAttributes.contains(attribute)) {
    177             throw new IllegalArgumentException();
    178         }
    179         mAttributes.put(attribute, value);
    180     }
    181 
    182     /**
    183      * Get the value of an attribute.
    184      *
    185      * @param attribute The name of the attribute.
    186      * @return The value or null if the attribute has not been set.
    187      * @throws IllegalArgumentException If the attribute is not in allowedAttributes.
    188      */
    189     protected Object getAttribute(String attribute) throws IllegalArgumentException {
    190         if (!mAllowedAttributes.contains(attribute)) {
    191             throw new IllegalArgumentException();
    192         }
    193         return mAttributes.get(attribute);
    194     }
    195 
    196     /**
    197      * Helper method to return if two objects are equal.
    198      *
    199      * @param object1 The first object
    200      * @param object2 The second object
    201      * @return True if object1 and object2 are both null or if object1 is equal to object2, false
    202      * otherwise.
    203      */
    204     static protected boolean areEqual(Object object1, Object object2) {
    205         return object1 == null ? object2 == null : object1.equals(object2);
    206     }
    207 
    208     /**
    209      * Helper method to return if two objects are consistent.
    210      *
    211      * @param object1 The first object
    212      * @param object2 The second object
    213      * @return True if either object1 or object2 is null or if object1 is equal to object2, false if
    214      * both objects are not null and not equal.
    215      */
    216     static protected boolean areConsistent(Object object1, Object object2) {
    217         return object1 == null || object2 == null ? true : object1.equals(object2);
    218     }
    219 
    220     /**
    221      * Helper method used for merging two objects.
    222      *
    223      * @param object1 The first object
    224      * @param object2 The second object
    225      * @return If both objects are null, then null, else the non-null item if both items are equal.
    226      * @throws ConflictingItemException If both objects are not null and they are not equal.
    227      */
    228     static protected Object mergeObjects(Object object1, Object object2)
    229             throws ConflictingItemException {
    230         if (!areConsistent(object1, object2)) {
    231             throw new ConflictingItemException(String.format("%s conflicts with %s", object1,
    232                     object2));
    233         }
    234         return object1 == null ? object2 : object1;
    235     }
    236 }
    237