1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002,2008 Oracle. All rights reserved. 5 * 6 * $Id: RawObject.java,v 1.1 2008/02/07 17:12:28 mark Exp $ 7 */ 8 9package com.sleepycat.persist.raw; 10 11import java.util.Arrays; 12import java.util.Map; 13import java.util.TreeSet; 14 15import com.sleepycat.persist.evolve.Conversion; 16import com.sleepycat.persist.model.EntityModel; 17 18/** 19 * A raw instance that can be used with a {@link RawStore} or {@link 20 * Conversion}. A <code>RawObject</code> is used to represent instances of 21 * complex types (persistent classes with fields), arrays, and enum values. It 22 * is not used to represent non-enum simple types, which are represented as 23 * simple objects. This includes primitives, which are represented as simple 24 * objects using their wrapper class. 25 * 26 * <p>{@code RawObject} objects are thread-safe. Multiple threads may safely 27 * call the methods of a shared {@code RawObject} object.</p> 28 * 29 * @author Mark Hayes 30 */ 31public class RawObject { 32 33 private static final String INDENT = " "; 34 35 private RawType type; 36 private Map<String,Object> values; 37 private Object[] elements; 38 private String enumConstant; 39 private RawObject superObject; 40 41 /** 42 * Creates a raw object with a given set of field values for a complex 43 * type. 44 * 45 * @param type the type of this raw object. 46 * 47 * @param values a map of field name to value for each declared field in 48 * the class, or null to create an empty map. Each value in the map is a 49 * {@link RawObject}, a {@link <a 50 * href="../model/Entity.html#simpleTypes">simple type</a>} instance, or 51 * null. 52 * 53 * @param superObject the instance of the superclass, or null if the 54 * superclass is {@code Object}. 55 * 56 * @throws IllegalArgumentException if the type argument is an array type. 57 */ 58 public RawObject(RawType type, 59 Map<String,Object> values, 60 RawObject superObject) { 61 if (type == null || values == null) { 62 throw new NullPointerException(); 63 } 64 this.type = type; 65 this.values = values; 66 this.superObject = superObject; 67 } 68 69 /** 70 * Creates a raw object with the given array elements for an array type. 71 * 72 * @param type the type of this raw object. 73 * 74 * @param elements an array of elements. Each element in the array is a 75 * {@link RawObject}, a {@link <a 76 * href="../model/Entity.html#simpleTypes">simple type</a>} instance, or 77 * null. 78 * 79 * @throws IllegalArgumentException if the type argument is not an array 80 * type. 81 */ 82 public RawObject(RawType type, Object[] elements) { 83 if (type == null || elements == null) { 84 throw new NullPointerException(); 85 } 86 this.type = type; 87 this.elements = elements; 88 } 89 90 /** 91 * Creates a raw object with the given enum value for an enum type. 92 * 93 * @param type the type of this raw object. 94 * 95 * @param enumConstant the String value of this enum constant; must be 96 * one of the Strings returned by {@link RawType#getEnumConstants}. 97 * 98 * @throws IllegalArgumentException if the type argument is not an array 99 * type. 100 */ 101 public RawObject(RawType type, String enumConstant) { 102 if (type == null || enumConstant == null) { 103 throw new NullPointerException(); 104 } 105 this.type = type; 106 this.enumConstant = enumConstant; 107 } 108 109 /** 110 * Returns the raw type information for this raw object. 111 * 112 * <p>Note that if this object is unevolved, the returned type may be 113 * different from the current type returned by {@link 114 * EntityModel#getRawType EntityModel.getRawType} for the same class name. 115 * This can only occur in a {@link Conversion#convert 116 * Conversion.convert}.</p> 117 */ 118 public RawType getType() { 119 return type; 120 } 121 122 /** 123 * Returns a map of field name to value for a complex type, or null for an 124 * array type or an enum type. The map contains a String key for each 125 * declared field in the class. Each value in the map is a {@link 126 * RawObject}, a {@link <a href="../model/Entity.html#simpleTypes">simple 127 * type</a>} instance, or null. 128 * 129 * <p>There will be an entry in the map for every field declared in this 130 * type, as determined by {@link RawType#getFields} for the type returned 131 * by {@link #getType}. Values in the map may be null for fields with 132 * non-primitive types.</p> 133 */ 134 public Map<String,Object> getValues() { 135 return values; 136 } 137 138 /** 139 * Returns the array of elements for an array type, or null for a complex 140 * type or an enum type. Each element in the array is a {@link RawObject}, 141 * a {@link <a href="../model/Entity.html#simpleTypes">simple type</a>} 142 * instance, or null. 143 */ 144 public Object[] getElements() { 145 return elements; 146 } 147 148 /** 149 * Returns the enum constant String for an enum type, or null for a complex 150 * type or an array type. The String returned will be one of the Strings 151 * returned by {@link RawType#getEnumConstants}. 152 */ 153 public String getEnum() { 154 return enumConstant; 155 } 156 157 /** 158 * Returns the instance of the superclass, or null if the superclass is 159 * {@code Object} or {@code Enum}. 160 */ 161 public RawObject getSuper() { 162 return superObject; 163 } 164 165 @Override 166 public boolean equals(Object other) { 167 if (other == this) { 168 return true; 169 } 170 if (!(other instanceof RawObject)) { 171 return false; 172 } 173 RawObject o = (RawObject) other; 174 if (type != o.type) { 175 return false; 176 } 177 if (!Arrays.deepEquals(elements, o.elements)) { 178 return false; 179 } 180 if (values != null) { 181 if (!values.equals(o.values)) { 182 return false; 183 } 184 } else { 185 if (o.values != null) { 186 return false; 187 } 188 } 189 if (superObject != null) { 190 if (!superObject.equals(o.superObject)) { 191 return false; 192 } 193 } else { 194 if (o.superObject != null) { 195 return false; 196 } 197 } 198 return true; 199 } 200 201 @Override 202 public int hashCode() { 203 return System.identityHashCode(type) + 204 Arrays.deepHashCode(elements) + 205 (values != null ? values.hashCode() : 0) + 206 (superObject != null ? superObject.hashCode() : 0); 207 } 208 209 @Override 210 public String toString() { 211 StringBuffer buf = new StringBuffer(500); 212 formatRawObject(buf, "", null, false); 213 return buf.toString(); 214 } 215 216 private void formatRawObject(StringBuffer buf, 217 String indent, 218 String id, 219 boolean isSuper) { 220 String indent2 = indent + INDENT; 221 String endTag; 222 buf.append(indent); 223 if (type.isArray()) { 224 buf.append("<Array"); 225 endTag = "</Array>"; 226 } else if (type.isEnum()) { 227 buf.append("<Enum"); 228 endTag = "</Enum>"; 229 } else if (isSuper) { 230 buf.append("<Super"); 231 endTag = "</Super>"; 232 } else { 233 buf.append("<Object"); 234 endTag = "</Object>"; 235 } 236 if (id != null) { 237 formatId(buf, id); 238 } 239 if (type.isArray()) { 240 buf.append(" length=\""); 241 buf.append(elements.length); 242 buf.append('"'); 243 } 244 buf.append(" class=\""); 245 buf.append(type.getClassName()); 246 buf.append("\">\n"); 247 248 if (superObject != null) { 249 superObject.formatRawObject(buf, indent2, null, true); 250 } 251 if (type.isArray()) { 252 for (int i = 0; i < elements.length; i += 1) { 253 formatValue(buf, indent2, String.valueOf(i), elements[i]); 254 } 255 } else if (type.isEnum()) { 256 buf.append(enumConstant); 257 } else { 258 TreeSet<String> keys = new TreeSet<String>(values.keySet()); 259 for (String name : keys) { 260 formatValue(buf, indent2, name, values.get(name)); 261 } 262 } 263 buf.append(indent); 264 buf.append(endTag); 265 buf.append("\n"); 266 } 267 268 private static void formatValue(StringBuffer buf, 269 String indent, 270 String id, 271 Object val) { 272 if (val == null) { 273 buf.append(indent); 274 buf.append("<Null"); 275 formatId(buf, id); 276 buf.append("/>\n"); 277 } else if (val instanceof RawObject) { 278 ((RawObject) val).formatRawObject(buf, indent, id, false); 279 } else { 280 buf.append(indent); 281 buf.append("<Value"); 282 formatId(buf, id); 283 buf.append(" class=\""); 284 buf.append(val.getClass().getName()); 285 buf.append("\">"); 286 buf.append(val.toString()); 287 buf.append("</Value>\n"); 288 } 289 } 290 291 private static void formatId(StringBuffer buf, String id) { 292 if (Character.isDigit(id.charAt(0))) { 293 buf.append(" index=\""); 294 } else { 295 buf.append(" field=\""); 296 } 297 buf.append(id); 298 buf.append('"'); 299 } 300} 301