1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002-2009 Oracle. All rights reserved. 5 * 6 * $Id$ 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 24 * instances of 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 (enumConstant != null) { 181 if (!enumConstant.equals(o.enumConstant)) { 182 return false; 183 } 184 } else { 185 if (o.enumConstant != null) { 186 return false; 187 } 188 } 189 if (values != null) { 190 if (!values.equals(o.values)) { 191 return false; 192 } 193 } else { 194 if (o.values != null) { 195 return false; 196 } 197 } 198 if (superObject != null) { 199 if (!superObject.equals(o.superObject)) { 200 return false; 201 } 202 } else { 203 if (o.superObject != null) { 204 return false; 205 } 206 } 207 return true; 208 } 209 210 @Override 211 public int hashCode() { 212 return System.identityHashCode(type) + 213 Arrays.deepHashCode(elements) + 214 (enumConstant != null ? enumConstant.hashCode() : 0) + 215 (values != null ? values.hashCode() : 0) + 216 (superObject != null ? superObject.hashCode() : 0); 217 } 218 219 /** 220 * Returns an XML representation of the raw object. 221 */ 222 @Override 223 public String toString() { 224 StringBuffer buf = new StringBuffer(500); 225 formatRawObject(buf, "", null, false); 226 return buf.toString(); 227 } 228 229 private void formatRawObject(StringBuffer buf, 230 String indent, 231 String id, 232 boolean isSuper) { 233 if (type.isEnum()) { 234 buf.append(indent); 235 buf.append("<Enum"); 236 formatId(buf, id); 237 buf.append(" class=\""); 238 buf.append(type.getClassName()); 239 buf.append("\" typeId=\""); 240 buf.append(type.getId()); 241 buf.append("\">"); 242 buf.append(enumConstant); 243 buf.append("</Enum>\n"); 244 } else { 245 String indent2 = indent + INDENT; 246 String endTag; 247 buf.append(indent); 248 if (type.isArray()) { 249 buf.append("<Array"); 250 endTag = "</Array>"; 251 } else if (isSuper) { 252 buf.append("<Super"); 253 endTag = "</Super>"; 254 } else { 255 buf.append("<Object"); 256 endTag = "</Object>"; 257 } 258 formatId(buf, id); 259 if (type.isArray()) { 260 buf.append(" length=\""); 261 buf.append(elements.length); 262 buf.append('"'); 263 } 264 buf.append(" class=\""); 265 buf.append(type.getClassName()); 266 buf.append("\" typeId=\""); 267 buf.append(type.getId()); 268 buf.append("\">\n"); 269 270 if (superObject != null) { 271 superObject.formatRawObject(buf, indent2, null, true); 272 } 273 if (type.isArray()) { 274 for (int i = 0; i < elements.length; i += 1) { 275 formatValue(buf, indent2, String.valueOf(i), elements[i]); 276 } 277 } else { 278 TreeSet<String> keys = new TreeSet<String>(values.keySet()); 279 for (String name : keys) { 280 formatValue(buf, indent2, name, values.get(name)); 281 } 282 } 283 buf.append(indent); 284 buf.append(endTag); 285 buf.append("\n"); 286 } 287 } 288 289 private static void formatValue(StringBuffer buf, 290 String indent, 291 String id, 292 Object val) { 293 if (val == null) { 294 buf.append(indent); 295 buf.append("<Null"); 296 formatId(buf, id); 297 buf.append("/>\n"); 298 } else if (val instanceof RawObject) { 299 ((RawObject) val).formatRawObject(buf, indent, id, false); 300 } else { 301 buf.append(indent); 302 buf.append("<Value"); 303 formatId(buf, id); 304 buf.append(" class=\""); 305 buf.append(val.getClass().getName()); 306 buf.append("\">"); 307 buf.append(val.toString()); 308 buf.append("</Value>\n"); 309 } 310 } 311 312 private static void formatId(StringBuffer buf, String id) { 313 if (id != null) { 314 if (Character.isDigit(id.charAt(0))) { 315 buf.append(" index=\""); 316 } else { 317 buf.append(" field=\""); 318 } 319 buf.append(id); 320 buf.append('"'); 321 } 322 } 323} 324