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 GenericItem(Set<String> allowedAttributes)33 protected GenericItem(Set<String> allowedAttributes) { 34 mAllowedAttributes = new HashSet<String>(); 35 mAllowedAttributes.addAll(allowedAttributes); 36 } 37 GenericItem(Set<String> allowedAttributes, Map<String, Object> attributes)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 merge(IItem other)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 */ mergeAttributes(IItem other, Set<String> attributes)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 isConsistent(IItem other)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 equals(Object other)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 hashCode()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 toJson()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 */ setAttribute(String attribute, Object value)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 */ getAttribute(String attribute)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 */ areEqual(Object object1, Object object2)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 */ areConsistent(Object object1, Object object2)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 */ mergeObjects(Object object1, Object object2)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