com.sleepycat.persist.impl.RecordInput Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of je Show documentation
Show all versions of je Show documentation
Berkley Database Java Edition - build and runtime support.
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2002, 2013 Oracle and/or its affiliates. All rights reserved.
*
*/
package com.sleepycat.persist.impl;
import java.util.HashMap;
import java.util.Map;
import com.sleepycat.bind.tuple.TupleInput;
import com.sleepycat.je.DatabaseEntry;
/**
* Implements EntityInput to read record key-data pairs. Extends TupleInput to
* implement the subset of TupleInput methods that are defined in the
* EntityInput interface.
*
* @author Mark Hayes
*/
class RecordInput extends TupleInput implements EntityInput {
/* Initial size of visited map. */
static final int VISITED_INIT_SIZE = 50;
/*
* Offset to indicate that the visited object is stored in the primary key
* byte array.
*/
static final int PRI_KEY_VISITED_OFFSET = Integer.MAX_VALUE - 1;
/* Used by RecordOutput to prevent illegal nested references. */
static final int PROHIBIT_REF_OFFSET = Integer.MAX_VALUE - 2;
/* Used by RecordInput to prevent illegal nested references. */
static final Object PROHIBIT_REF_OBJECT = new Object();
static final String PROHIBIT_NESTED_REF_MSG =
"Cannot embed a reference to a proxied object in the proxy; for " +
"example, a collection may not be an element of the collection " +
"because collections are proxied";
private Catalog catalog;
private boolean rawAccess;
private Map visited;
private DatabaseEntry priKeyEntry;
private int priKeyFormatId;
private boolean newStringFormat;
/**
* Creates a new input with a empty/null visited map.
*/
RecordInput(Catalog catalog,
boolean rawAccess,
DatabaseEntry priKeyEntry,
int priKeyFormatId,
byte[] buffer,
int offset,
int length) {
super(buffer, offset, length);
this.catalog = catalog;
this.rawAccess = rawAccess;
this.priKeyEntry = priKeyEntry;
this.priKeyFormatId = priKeyFormatId;
this.visited = new HashMap(VISITED_INIT_SIZE);
}
/**
* Copy contructor where a new offset can be specified.
*/
private RecordInput(RecordInput other, int offset) {
this(other.catalog, other.rawAccess, other.priKeyEntry,
other.priKeyFormatId, other.buf, offset, other.len);
visited = other.visited;
}
/**
* Copy contructor where a DatabaseEntry can be specified.
*/
private RecordInput(RecordInput other, DatabaseEntry entry) {
this(other.catalog, other.rawAccess, other.priKeyEntry,
other.priKeyFormatId, entry.getData(), entry.getOffset(),
entry.getSize());
visited = other.visited;
}
/**
* @see EntityInput#getCatalog
*/
public Catalog getCatalog() {
return catalog;
}
/**
* @see EntityInput#isRawAccess
*/
public boolean isRawAccess() {
return rawAccess;
}
/**
* @see EntityInput#setRawAccess
*/
public boolean setRawAccess(boolean rawAccessParam) {
boolean original = rawAccess;
rawAccess = rawAccessParam;
return original;
}
/**
* @see EntityInput#readObject
*/
public Object readObject()
throws RefreshException {
/* Save the current offset before reading the format ID. */
Integer visitedOffset = off;
RecordInput useInput = this;
int formatId = readPackedInt();
Object o = null;
/* For a zero format ID, return a null instance. */
if (formatId == Format.ID_NULL) {
return null;
}
/* For a negative format ID, lookup an already visited instance. */
if (formatId < 0) {
int offset = (-(formatId + 1));
o = visited.get(offset);
if (o == RecordInput.PROHIBIT_REF_OBJECT) {
throw new IllegalArgumentException
(RecordInput.PROHIBIT_NESTED_REF_MSG);
}
if (o != null) {
/* Return a previously visited object. */
return o;
} else {
/*
* When reading starts from a non-zero offset, we may have to
* go back in the stream and read the referenced object. This
* happens when reading secondary key fields.
*/
visitedOffset = offset;
if (offset == RecordInput.PRI_KEY_VISITED_OFFSET) {
assert priKeyEntry != null && priKeyFormatId > 0;
useInput = new RecordInput(this, priKeyEntry);
formatId = priKeyFormatId;
} else {
useInput = new RecordInput(this, offset);
formatId = useInput.readPackedInt();
}
}
}
/*
* Add a visted object slot that prohibits nested references to this
* object during the call to Reader.newInstance below. The newInstance
* method is allowed to read nested fields (in which case
* Reader.readObject further below does nothing) under certain
* conditions, but under these conditions we do not support nested
* references to the parent object. [#15815]
*/
visited.put(visitedOffset, RecordInput.PROHIBIT_REF_OBJECT);
/* Create the object using the format indicated. */
Format format = catalog.getFormat(formatId, true /*expectStored*/);
Reader reader = format.getReader();
o = reader.newInstance(useInput, rawAccess);
/*
* Set the newly created object in the map of visited objects. This
* must be done before calling Reader.readObject, which allows the
* object to contain a reference to itself.
*/
visited.put(visitedOffset, o);
/*
* Finish reading the object. Then replace it in the visited map in
* case a converted object is returned by readObject.
*/
Object o2 = reader.readObject(o, useInput, rawAccess);
if (o != o2) {
visited.put(visitedOffset, o2);
}
return o2;
}
/**
* @see EntityInput#readKeyObject
*/
public Object readKeyObject(Format format)
throws RefreshException {
/* Create and read the object using the given key format. */
Reader reader = format.getReader();
Object o = reader.newInstance(this, rawAccess);
return reader.readObject(o, this, rawAccess);
}
/**
* @see EntityInput#readStringObject
*/
public Object readStringObject()
throws RefreshException {
if (!newStringFormat) {
return readObject();
}
return readString();
}
/**
* Called when copying secondary keys, for an input that is positioned on
* the secondary key field. Handles references to previously occurring
* objects, returning a different RecordInput than this one if appropriate.
*/
KeyLocation getKeyLocation(Format fieldFormat)
throws RefreshException {
RecordInput input = this;
if (fieldFormat.getId() == Format.ID_STRING && newStringFormat) {
/*
* In new JE version, we do not store format ID for String data,
* So we have to read the real String data to see if the String
* data is null or not. [#19247]
*/
int len = input.getStringByteLength();
String strKey = input.readString();
input.skipFast(0 - len);
if(strKey == null) {
/* String key field is null. */
return null;
}
} else if (!fieldFormat.isPrimitive()) {
int formatId = input.readPackedInt();
if (formatId == Format.ID_NULL) {
/* Key field is null. */
return null;
}
if (formatId < 0) {
int offset = (-(formatId + 1));
if (offset == RecordInput.PRI_KEY_VISITED_OFFSET) {
assert priKeyEntry != null && priKeyFormatId > 0;
input = new RecordInput(this, priKeyEntry);
formatId = priKeyFormatId;
} else {
input = new RecordInput(this, offset);
formatId = input.readPackedInt();
}
}
fieldFormat = catalog.getFormat(formatId, true /*expectStored*/);
}
/* Key field is non-null. */
return new KeyLocation(input, fieldFormat);
}
/**
* @see EntityInput#registerPriKeyObject
*/
public void registerPriKeyObject(Object o) {
/*
* PRI_KEY_VISITED_OFFSET is used as the visited offset to indicate
* that the visited object is stored in the primary key byte array.
*/
visited.put(RecordInput.PRI_KEY_VISITED_OFFSET, o);
}
/**
* @see EntityInput#registerPriKeyObject
*/
public void registerPriStringKeyObject(Object o) {
/*
* In JE 5.0 and later, String is treated as a primitive type, so a
* String object does not need to be registered. But in earlier
* versions, Strings are treated as any other object and must be
* registered. [#19247]
*/
if (!newStringFormat) {
visited.put(RecordInput.PRI_KEY_VISITED_OFFSET, o);
}
}
/**
* Registers the top level entity before reading it, to allow nested fields
* to reference their parent entity. [#17525]
*/
public void registerEntity(Object entity, int initialOffset) {
visited.put(initialOffset, entity);
}
/**
* Registers the entity format before reading it, so that old-format String
* fields can be read properly. [#19247]
*/
public void registerEntityFormat(Format entityFormat) {
newStringFormat = entityFormat.getNewStringFormat();
}
/**
* @see EntityInput#skipField
*/
public void skipField(Format declaredFormat)
throws RefreshException {
if (declaredFormat != null && declaredFormat.isPrimitive()) {
declaredFormat.skipContents(this);
} else if (declaredFormat != null &&
declaredFormat.getId() == Format.ID_STRING &&
newStringFormat) {
/*
* In the new JE version, we treat String as primitive, and will
* not store format ID for String data. [#19247]
*/
declaredFormat.skipContents(this);
} else {
int formatId = readPackedInt();
if (formatId > 0) {
Format format =
catalog.getFormat(formatId, true /*expectStored*/);
format.skipContents(this);
}
}
}
public int readArrayLength() {
return readPackedInt();
}
public int readEnumConstant(String[] names) {
return readPackedInt();
}
}