1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002,2008 Oracle. All rights reserved. 5 * 6 * $Id: RecordInput.java,v 1.1 2008/02/07 17:12:27 mark Exp $ 7 */ 8 9package com.sleepycat.persist.impl; 10 11import com.sleepycat.bind.tuple.TupleInput; 12import com.sleepycat.db.DatabaseEntry; 13 14/** 15 * Implements EntityInput to read record key-data pairs. Extends TupleInput to 16 * implement the subset of TupleInput methods that are defined in the 17 * EntityInput interface. 18 * 19 * @author Mark Hayes 20 */ 21class RecordInput extends TupleInput implements EntityInput { 22 23 private Catalog catalog; 24 private boolean rawAccess; 25 private VisitedObjects visited; 26 private DatabaseEntry priKeyEntry; 27 private int priKeyFormatId; 28 29 /** 30 * Creates a new input with a empty/null VisitedObjects set. 31 */ 32 RecordInput(Catalog catalog, 33 boolean rawAccess, 34 DatabaseEntry priKeyEntry, 35 int priKeyFormatId, 36 byte[] buffer, 37 int offset, 38 int length) { 39 super(buffer, offset, length); 40 this.catalog = catalog; 41 this.rawAccess = rawAccess; 42 this.priKeyEntry = priKeyEntry; 43 this.priKeyFormatId = priKeyFormatId; 44 } 45 46 /** 47 * Copy contructor where a new offset can be specified. 48 */ 49 private RecordInput(RecordInput other, int offset) { 50 this(other.catalog, other.rawAccess, other.priKeyEntry, 51 other.priKeyFormatId, other.buf, offset, other.len); 52 visited = other.visited; 53 } 54 55 /** 56 * Copy contructor where a DatabaseEntry can be specified. 57 */ 58 private RecordInput(RecordInput other, DatabaseEntry entry) { 59 this(other.catalog, other.rawAccess, other.priKeyEntry, 60 other.priKeyFormatId, entry.getData(), entry.getOffset(), 61 entry.getSize()); 62 visited = other.visited; 63 } 64 65 /** 66 * @see EntityInput#getCatalog 67 */ 68 public Catalog getCatalog() { 69 return catalog; 70 } 71 72 /** 73 * @see EntityInput#isRawAccess 74 */ 75 public boolean isRawAccess() { 76 return rawAccess; 77 } 78 79 /** 80 * @see EntityInput#setRawAccess 81 */ 82 public boolean setRawAccess(boolean rawAccessParam) { 83 boolean original = rawAccess; 84 rawAccess = rawAccessParam; 85 return original; 86 } 87 88 /** 89 * @see EntityInput#readObject 90 */ 91 public Object readObject() { 92 93 /* Save the current offset before reading the format ID. */ 94 int visitedOffset = off; 95 RecordInput useInput = this; 96 int formatId = readPackedInt(); 97 Object o = null; 98 99 /* For a zero format ID, return a null instance. */ 100 if (formatId == Format.ID_NULL) { 101 return null; 102 } 103 104 /* For a negative format ID, lookup an already visited instance. */ 105 if (formatId < 0) { 106 int offset = (-(formatId + 1)); 107 if (visited != null) { 108 o = visited.getObject(offset); 109 } 110 if (o == VisitedObjects.PROHIBIT_REF_OBJECT) { 111 throw new IllegalArgumentException 112 (VisitedObjects.PROHIBIT_NESTED_REF_MSG); 113 } 114 if (o != null) { 115 /* Return a previously visited object. */ 116 return o; 117 } else { 118 119 /* 120 * When reading starts from a non-zero offset, we may have to 121 * go back in the stream and read the referenced object. This 122 * happens when reading secondary key fields. 123 */ 124 visitedOffset = offset; 125 if (offset == VisitedObjects.PRI_KEY_VISITED_OFFSET) { 126 assert priKeyEntry != null && priKeyFormatId > 0; 127 useInput = new RecordInput(this, priKeyEntry); 128 formatId = priKeyFormatId; 129 } else { 130 useInput = new RecordInput(this, offset); 131 formatId = useInput.readPackedInt(); 132 } 133 } 134 } 135 136 /* 137 * Add a visted object slot that prohibits nested references to this 138 * object during the call to Reader.newInstance below. The newInstance 139 * method is allowed to read nested fields (in which case 140 * Reader.readObject further below does nothing) under certain 141 * conditions, but under these conditions we do not support nested 142 * references to the parent object. [#15815] 143 */ 144 if (visited == null) { 145 visited = new VisitedObjects(); 146 } 147 int visitedIndex = 148 visited.add(VisitedObjects.PROHIBIT_REF_OBJECT, visitedOffset); 149 150 /* Create the object using the format indicated. */ 151 Format format = catalog.getFormat(formatId); 152 Reader reader = format.getReader(); 153 o = reader.newInstance(useInput, rawAccess); 154 155 /* 156 * Set the newly created object in the set of visited objects. This 157 * must be done before calling Reader.readObject, which allows the 158 * object to contain a reference to itself. 159 */ 160 visited.setObject(visitedIndex, o); 161 162 /* 163 * Finish reading the object. Then replace it in the visited list in 164 * case a converted object is returned by readObject. 165 */ 166 Object o2 = reader.readObject(o, useInput, rawAccess); 167 if (o != o2) { 168 visited.replaceObject(o, o2); 169 } 170 return o2; 171 } 172 173 /** 174 * @see EntityInput#readKeyObject 175 */ 176 public Object readKeyObject(Format format) { 177 178 /* Create and read the object using the given key format. */ 179 Reader reader = format.getReader(); 180 Object o = reader.newInstance(this, rawAccess); 181 return reader.readObject(o, this, rawAccess); 182 } 183 184 /** 185 * Called when copying secondary keys, for an input that is positioned on 186 * the secondary key field. Handles references to previously occurring 187 * objects, returning a different RecordInput than this one if appropriate. 188 */ 189 KeyLocation getKeyLocation(Format fieldFormat) { 190 RecordInput input = this; 191 if (!fieldFormat.isPrimitive()) { 192 int formatId = input.readPackedInt(); 193 if (formatId == Format.ID_NULL) { 194 /* Key field is null. */ 195 return null; 196 } 197 if (formatId < 0) { 198 int offset = (-(formatId + 1)); 199 if (offset == VisitedObjects.PRI_KEY_VISITED_OFFSET) { 200 assert priKeyEntry != null && priKeyFormatId > 0; 201 input = new RecordInput(this, priKeyEntry); 202 formatId = priKeyFormatId; 203 } else { 204 input = new RecordInput(this, offset); 205 formatId = input.readPackedInt(); 206 } 207 } 208 fieldFormat = catalog.getFormat(formatId); 209 } 210 /* Key field is non-null. */ 211 return new KeyLocation(input, fieldFormat); 212 } 213 214 /** 215 * @see EntityInput#registerPriKeyObject 216 */ 217 public void registerPriKeyObject(Object o) { 218 219 /* 220 * PRI_KEY_VISITED_OFFSET is used as the visited offset to indicate 221 * that the visited object is stored in the primary key byte array. 222 */ 223 if (visited == null) { 224 visited = new VisitedObjects(); 225 } 226 visited.add(o, VisitedObjects.PRI_KEY_VISITED_OFFSET); 227 } 228 229 /** 230 * @see EntityInput#skipField 231 */ 232 public void skipField(Format declaredFormat) { 233 if (declaredFormat != null && declaredFormat.isPrimitive()) { 234 declaredFormat.skipContents(this); 235 } else { 236 int formatId = readPackedInt(); 237 if (formatId > 0) { 238 Format format = catalog.getFormat(formatId); 239 format.skipContents(this); 240 } 241 } 242 } 243 244 public int readArrayLength() { 245 return readPackedInt(); 246 } 247 248 public int readEnumConstant(String[] names) { 249 return readPackedInt(); 250 } 251} 252