com.sleepycat.persist.impl.RecordInput Maven / Gradle / Ivy
The newest version!
/*-
* Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
*
* This file was distributed by Oracle as part of a version of Oracle Berkeley
* DB Java Edition made available at:
*
* http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
*
* Please see the LICENSE file included in the top-level directory of the
* appropriate version of Oracle Berkeley DB Java Edition for a copy of the
* license and additional information.
*/
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();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy