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.impl; 10 11import java.lang.reflect.Array; 12import java.util.IdentityHashMap; 13import java.util.Map; 14import java.util.Set; 15 16import com.sleepycat.db.DatabaseEntry; 17import com.sleepycat.persist.model.EntityModel; 18import com.sleepycat.persist.raw.RawObject; 19 20/** 21 * An array of objects having a specified number of dimensions. All 22 * multidimensional arrays are handled by this class, since even a primitive 23 * array of more than one dimension is an array of objects, where the component 24 * objects may be primitive arrays. The {@link PrimitiveArrayFormat} class 25 * handles primitive arrays of one dimension only. 26 * 27 * In this class, and {@link PrimitiveArrayFormat}, we resort to using 28 * reflection to allocate multidimensional arrays. If there is a need for it, 29 * reflection could be avoided in the future by generating code as new array 30 * formats are encountered. 31 * 32 * @author Mark Hayes 33 */ 34public class ObjectArrayFormat extends Format { 35 36 private static final long serialVersionUID = 4317004346690441892L; 37 38 private Format componentFormat; 39 private int nDimensions; 40 private transient Format useComponentFormat; 41 42 ObjectArrayFormat(Class type) { 43 super(type); 44 String name = getClassName(); 45 for (nDimensions = 0; 46 name.charAt(nDimensions) == '['; 47 nDimensions += 1) { 48 } 49 } 50 51 @Override 52 public boolean isArray() { 53 return true; 54 } 55 56 @Override 57 public int getDimensions() { 58 return nDimensions; 59 } 60 61 @Override 62 public Format getComponentType() { 63 return (useComponentFormat != null) ? 64 useComponentFormat : componentFormat; 65 } 66 67 @Override 68 void collectRelatedFormats(Catalog catalog, 69 Map<String,Format> newFormats) { 70 Class cls = getType().getComponentType(); 71 catalog.createFormat(cls, newFormats); 72 } 73 74 @Override 75 void initialize(Catalog catalog, EntityModel model, int initVersion) { 76 /* Set the component format for a new (never initialized) format. */ 77 if (componentFormat == null) { 78 Class cls = getType().getComponentType(); 79 componentFormat = catalog.getFormat(cls.getName()); 80 } 81 useComponentFormat = componentFormat.getLatestVersion(); 82 } 83 84 @Override 85 boolean isAssignableTo(Format format) { 86 if (super.isAssignableTo(format)) { 87 return true; 88 } 89 if (format instanceof ObjectArrayFormat) { 90 ObjectArrayFormat other = (ObjectArrayFormat) format; 91 if (useComponentFormat.isAssignableTo(other.useComponentFormat)) { 92 return true; 93 } 94 } 95 return false; 96 } 97 98 @Override 99 Object newArray(int len) { 100 return Array.newInstance(getType(), len); 101 } 102 103 @Override 104 public Object newInstance(EntityInput input, boolean rawAccess) { 105 int len = input.readArrayLength(); 106 if (rawAccess) { 107 return new RawObject(this, new Object[len]); 108 } else { 109 return useComponentFormat.newArray(len); 110 } 111 } 112 113 @Override 114 public Object readObject(Object o, EntityInput input, boolean rawAccess) { 115 Object[] a; 116 if (rawAccess) { 117 a = ((RawObject) o).getElements(); 118 } else { 119 a = (Object[]) o; 120 } 121 for (int i = 0; i < a.length; i += 1) { 122 a[i] = input.readObject(); 123 } 124 return o; 125 } 126 127 @Override 128 void writeObject(Object o, EntityOutput output, boolean rawAccess) { 129 Object[] a; 130 if (rawAccess) { 131 a = ((RawObject) o).getElements(); 132 } else { 133 a = (Object[]) o; 134 } 135 output.writeArrayLength(a.length); 136 for (int i = 0; i < a.length; i += 1) { 137 output.writeObject(a[i], useComponentFormat); 138 } 139 } 140 141 @Override 142 Object convertRawObject(Catalog catalog, 143 boolean rawAccess, 144 RawObject rawObject, 145 IdentityHashMap converted) { 146 RawArrayInput input = new RawArrayInput 147 (catalog, rawAccess, converted, rawObject, useComponentFormat); 148 Object a = newInstance(input, rawAccess); 149 converted.put(rawObject, a); 150 return readObject(a, input, rawAccess); 151 } 152 153 @Override 154 void skipContents(RecordInput input) { 155 int len = input.readPackedInt(); 156 for (int i = 0; i < len; i += 1) { 157 input.skipField(useComponentFormat); 158 } 159 } 160 161 @Override 162 void copySecMultiKey(RecordInput input, Format keyFormat, Set results) { 163 int len = input.readPackedInt(); 164 for (int i = 0; i < len; i += 1) { 165 KeyLocation loc = input.getKeyLocation(useComponentFormat); 166 if (loc == null) { 167 throw new IllegalArgumentException 168 ("Secondary key values in array may not be null"); 169 } 170 if (loc.format != useComponentFormat) { 171 throw new IllegalStateException 172 (useComponentFormat.getClassName()); 173 } 174 int off1 = loc.input.getBufferOffset(); 175 useComponentFormat.skipContents(loc.input); 176 int off2 = loc.input.getBufferOffset(); 177 DatabaseEntry entry = new DatabaseEntry 178 (loc.input.getBufferBytes(), off1, off2 - off1); 179 results.add(entry); 180 } 181 } 182 183 @Override 184 boolean evolve(Format newFormat, Evolver evolver) { 185 186 /* 187 * When the class name of the component changes, we need a new format 188 * that references it. Otherwise, don't propogate changes from 189 * components upward to their arrays. 190 */ 191 Format latest = componentFormat.getLatestVersion(); 192 if (latest != componentFormat && 193 !latest.getClassName().equals(componentFormat.getClassName())) { 194 evolver.useEvolvedFormat(this, newFormat, newFormat); 195 } else { 196 evolver.useOldFormat(this, newFormat); 197 } 198 return true; 199 } 200} 201