001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: RecordInput.java,v 1.4.2.4 2008/01/07 15:14:20 cwl Exp $
007: */
008:
009: package com.sleepycat.persist.impl;
010:
011: import com.sleepycat.bind.tuple.TupleInput;
012: import com.sleepycat.je.DatabaseEntry;
013:
014: /**
015: * Implements EntityInput to read record key-data pairs. Extends TupleInput to
016: * implement the subset of TupleInput methods that are defined in the
017: * EntityInput interface.
018: *
019: * @author Mark Hayes
020: */
021: class RecordInput extends TupleInput implements EntityInput {
022:
023: private Catalog catalog;
024: private boolean rawAccess;
025: private VisitedObjects visited;
026: private DatabaseEntry priKeyEntry;
027: private int priKeyFormatId;
028:
029: /**
030: * Creates a new input with a empty/null VisitedObjects set.
031: */
032: RecordInput(Catalog catalog, boolean rawAccess,
033: DatabaseEntry priKeyEntry, int priKeyFormatId,
034: byte[] buffer, int offset, int length) {
035: super (buffer, offset, length);
036: this .catalog = catalog;
037: this .rawAccess = rawAccess;
038: this .priKeyEntry = priKeyEntry;
039: this .priKeyFormatId = priKeyFormatId;
040: }
041:
042: /**
043: * Copy contructor where a new offset can be specified.
044: */
045: private RecordInput(RecordInput other, int offset) {
046: this (other.catalog, other.rawAccess, other.priKeyEntry,
047: other.priKeyFormatId, other.buf, offset, other.len);
048: visited = other.visited;
049: }
050:
051: /**
052: * Copy contructor where a DatabaseEntry can be specified.
053: */
054: private RecordInput(RecordInput other, DatabaseEntry entry) {
055: this (other.catalog, other.rawAccess, other.priKeyEntry,
056: other.priKeyFormatId, entry.getData(), entry
057: .getOffset(), entry.getSize());
058: visited = other.visited;
059: }
060:
061: /**
062: * @see EntityInput#getCatalog
063: */
064: public Catalog getCatalog() {
065: return catalog;
066: }
067:
068: /**
069: * @see EntityInput#isRawAccess
070: */
071: public boolean isRawAccess() {
072: return rawAccess;
073: }
074:
075: /**
076: * @see EntityInput#setRawAccess
077: */
078: public boolean setRawAccess(boolean rawAccessParam) {
079: boolean original = rawAccess;
080: rawAccess = rawAccessParam;
081: return original;
082: }
083:
084: /**
085: * @see EntityInput#readObject
086: */
087: public Object readObject() {
088:
089: /* Save the current offset before reading the format ID. */
090: int visitedOffset = off;
091: RecordInput useInput = this ;
092: int formatId = readPackedInt();
093: Object o = null;
094:
095: /* For a zero format ID, return a null instance. */
096: if (formatId == Format.ID_NULL) {
097: return null;
098: }
099:
100: /* For a negative format ID, lookup an already visited instance. */
101: if (formatId < 0) {
102: int offset = (-(formatId + 1));
103: if (visited != null) {
104: o = visited.getObject(offset);
105: }
106: if (o == VisitedObjects.PROHIBIT_REF_OBJECT) {
107: throw new IllegalArgumentException(
108: VisitedObjects.PROHIBIT_NESTED_REF_MSG);
109: }
110: if (o != null) {
111: /* Return a previously visited object. */
112: return o;
113: } else {
114:
115: /*
116: * When reading starts from a non-zero offset, we may have to
117: * go back in the stream and read the referenced object. This
118: * happens when reading secondary key fields.
119: */
120: visitedOffset = offset;
121: if (offset == VisitedObjects.PRI_KEY_VISITED_OFFSET) {
122: assert priKeyEntry != null && priKeyFormatId > 0;
123: useInput = new RecordInput(this , priKeyEntry);
124: formatId = priKeyFormatId;
125: } else {
126: useInput = new RecordInput(this , offset);
127: formatId = useInput.readPackedInt();
128: }
129: }
130: }
131:
132: /*
133: * Add a visted object slot that prohibits nested references to this
134: * object during the call to Reader.newInstance below. The newInstance
135: * method is allowed to read nested fields (in which case
136: * Reader.readObject further below does nothing) under certain
137: * conditions, but under these conditions we do not support nested
138: * references to the parent object. [#15815]
139: */
140: if (visited == null) {
141: visited = new VisitedObjects();
142: }
143: int visitedIndex = visited.add(
144: VisitedObjects.PROHIBIT_REF_OBJECT, visitedOffset);
145:
146: /* Create the object using the format indicated. */
147: Format format = catalog.getFormat(formatId);
148: Reader reader = format.getReader();
149: o = reader.newInstance(useInput, rawAccess);
150:
151: /*
152: * Set the newly created object in the set of visited objects. This
153: * must be done before calling Reader.readObject, which allows the
154: * object to contain a reference to itself.
155: */
156: visited.setObject(visitedIndex, o);
157:
158: /*
159: * Finish reading the object. Then replace it in the visited list in
160: * case a converted object is returned by readObject.
161: */
162: Object o2 = reader.readObject(o, useInput, rawAccess);
163: if (o != o2) {
164: visited.replaceObject(o, o2);
165: }
166: return o2;
167: }
168:
169: /**
170: * @see EntityInput#readKeyObject
171: */
172: public Object readKeyObject(Format format) {
173:
174: /* Create and read the object using the given key format. */
175: Reader reader = format.getReader();
176: Object o = reader.newInstance(this , rawAccess);
177: return reader.readObject(o, this , rawAccess);
178: }
179:
180: /**
181: * Called when copying secondary keys, for an input that is positioned on
182: * the secondary key field. Handles references to previously occurring
183: * objects, returning a different RecordInput than this one if appropriate.
184: */
185: KeyLocation getKeyLocation(Format fieldFormat) {
186: RecordInput input = this ;
187: if (!fieldFormat.isPrimitive()) {
188: int formatId = input.readPackedInt();
189: if (formatId == Format.ID_NULL) {
190: /* Key field is null. */
191: return null;
192: }
193: if (formatId < 0) {
194: int offset = (-(formatId + 1));
195: if (offset == VisitedObjects.PRI_KEY_VISITED_OFFSET) {
196: assert priKeyEntry != null && priKeyFormatId > 0;
197: input = new RecordInput(this , priKeyEntry);
198: formatId = priKeyFormatId;
199: } else {
200: input = new RecordInput(this , offset);
201: formatId = input.readPackedInt();
202: }
203: }
204: fieldFormat = catalog.getFormat(formatId);
205: }
206: /* Key field is non-null. */
207: return new KeyLocation(input, fieldFormat);
208: }
209:
210: /**
211: * @see EntityInput#registerPriKeyObject
212: */
213: public void registerPriKeyObject(Object o) {
214:
215: /*
216: * PRI_KEY_VISITED_OFFSET is used as the visited offset to indicate
217: * that the visited object is stored in the primary key byte array.
218: */
219: if (visited == null) {
220: visited = new VisitedObjects();
221: }
222: visited.add(o, VisitedObjects.PRI_KEY_VISITED_OFFSET);
223: }
224:
225: /**
226: * @see EntityInput#skipField
227: */
228: public void skipField(Format declaredFormat) {
229: if (declaredFormat != null && declaredFormat.isPrimitive()) {
230: declaredFormat.skipContents(this );
231: } else {
232: int formatId = readPackedInt();
233: if (formatId > 0) {
234: Format format = catalog.getFormat(formatId);
235: format.skipContents(this );
236: }
237: }
238: }
239:
240: public int readArrayLength() {
241: return readPackedInt();
242: }
243:
244: public int readEnumConstant(String[] names) {
245: return readPackedInt();
246: }
247: }
|