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