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