com.sleepycat.persist.impl.CompositeKeyFormat 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.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import com.sleepycat.compat.DbCompat;
import com.sleepycat.persist.model.ClassMetadata;
import com.sleepycat.persist.model.EntityModel;
import com.sleepycat.persist.model.FieldMetadata;
import com.sleepycat.persist.raw.RawField;
import com.sleepycat.persist.raw.RawObject;
/**
* Format for a composite key class.
*
* This class is similar to ComplexFormat in that a composite key class and
* other complex classes have fields, and the Accessor interface is used to
* access those fields. Composite key classes are different in the following
* ways:
*
* - The superclass must be Object. No inheritance is allowed.
*
* - All instance fields must be annotated with @KeyField, which determines
* their order in the data bytes.
*
* - Although fields may be reference types (primitive wrappers or other simple
* reference types), they are stored as if they were primitives. No object
* format ID is stored, and the class of the object must be the declared
* classs of the field; i.e., no polymorphism is allowed for key fields.
* In other words, a composite key is stored as an ordinary tuple as defined
* in the com.sleepycat.bind.tuple package. This keeps the key small and
* gives it a well defined sort order.
*
* - If the key class implements Comparable, it is called by the Database
* btree comparator. It must therefore be available during JE recovery,
* before the store and catalog have been opened. To support this, this
* format can be constructed during recovery. A SimpleCatalog singleton
* instance is used to provide a catalog of simple types that is used by
* the composite key format.
*
* - When interacting with the Accessor, the composite key format treats the
* Accessor's non-key fields as its key fields. The Accessor's key fields
* are secondary keys, while the composite format's key fields are the
* component parts of a single key.
*
* @author Mark Hayes
*/
public class CompositeKeyFormat extends Format {
private static final long serialVersionUID = 306843428409314630L;
private ClassMetadata metadata;
private List fields;
private transient Accessor objAccessor;
private transient Accessor rawAccessor;
private transient volatile Map rawFields;
private transient volatile FieldInfo[] rawInputFields;
static String[] getFieldNameArray(List list) {
int index = 0;
String[] a = new String[list.size()];
for (FieldMetadata f : list) {
a[index] = f.getName();
index += 1;
}
return a;
}
/**
* Creates a new composite key format.
*/
CompositeKeyFormat(Catalog catalog,
Class cls,
ClassMetadata metadata,
List fieldMeta) {
this(catalog, cls, metadata, getFieldNameArray(fieldMeta));
}
/**
* Reconsistitues a composite key format after a PersistComparator is
* deserialized.
*/
CompositeKeyFormat(Catalog catalog, Class cls, String[] fieldNames) {
this(catalog, cls, null /*metadata*/, fieldNames);
}
private CompositeKeyFormat(Catalog catalog,
Class cls,
ClassMetadata metadata,
String[] fieldNames) {
super(catalog, cls);
this.metadata = metadata;
/* Check that the superclass is Object. */
Class superCls = cls.getSuperclass();
if (superCls != Object.class) {
throw new IllegalArgumentException
("Composite key class must be derived from Object: " +
cls.getName());
}
/* Populate fields list in fieldNames order. */
List instanceFields =
FieldInfo.getInstanceFields(cls, metadata);
fields = new ArrayList(instanceFields.size());
for (String fieldName : fieldNames) {
FieldInfo field = null;
for (FieldInfo tryField : instanceFields) {
if (fieldName.equals(tryField.getName())) {
field = tryField;
break;
}
}
if (field == null) {
throw new IllegalArgumentException
("Composite key field is not an instance field: " +
getClassName() + '.' + fieldName);
}
fields.add(field);
instanceFields.remove(field);
Class fieldCls = field.getFieldClass(getCatalog());
if (!SimpleCatalog.isSimpleType(fieldCls) &&
!fieldCls.isEnum()) {
throw new IllegalArgumentException
("Composite key field is not a simple type or enum: " +
getClassName() + '.' + fieldName);
}
}
if (instanceFields.size() > 0) {
throw new IllegalArgumentException
("All composite key instance fields must be key fields: " +
getClassName() + '.' + instanceFields.get(0).getName());
}
}
List getFieldInfo() {
return fields;
}
@Override
void migrateFromBeta(Map formatMap) {
super.migrateFromBeta(formatMap);
for (FieldInfo field : fields) {
field.migrateFromBeta(formatMap);
}
}
@Override
boolean isModelClass() {
return true;
}
@Override
public ClassMetadata getClassMetadata() {
if (metadata == null) {
throw DbCompat.unexpectedState(getClassName());
}
return metadata;
}
@Override
public Map getFields() {
/*
* Lazily create the raw type information. Synchronization is not
* required since this object is immutable. If by chance we create two
* maps when two threads execute this block, no harm is done. But be
* sure to assign the rawFields field only after the map is fully
* populated.
*/
if (rawFields == null) {
Map map = new HashMap();
for (RawField field : fields) {
map.put(field.getName(), field);
}
rawFields = map;
}
return rawFields;
}
@Override
void collectRelatedFormats(Catalog catalog,
Map newFormats) {
/* Collect field formats. */
for (FieldInfo field : fields) {
field.collectRelatedFormats(catalog, newFormats);
}
}
@Override
void initialize(Catalog catalog, EntityModel model, int initVersion) {
/* Initialize all fields. */
for (FieldInfo field : fields) {
field.initialize(catalog, model, initVersion);
}
/* Create the accessor. */
Class type = getType();
if (type != null) {
if (EnhancedAccessor.isEnhanced(type)) {
objAccessor = new EnhancedAccessor(catalog, type, fields);
} else {
objAccessor = new ReflectionAccessor(catalog, type, fields);
}
}
rawAccessor = new RawAccessor(this, fields);
}
@Override
Object newArray(int len) {
return objAccessor.newArray(len);
}
@Override
public Object newInstance(EntityInput input, boolean rawAccess) {
Accessor accessor = rawAccess ? rawAccessor : objAccessor;
return accessor.newInstance();
}
@Override
public Object readObject(Object o, EntityInput input, boolean rawAccess)
throws RefreshException {
Accessor accessor = rawAccess ? rawAccessor : objAccessor;
accessor.readCompositeKeyFields(o, input);
return o;
}
@Override
void writeObject(Object o, EntityOutput output, boolean rawAccess)
throws RefreshException {
Accessor accessor = rawAccess ? rawAccessor : objAccessor;
accessor.writeCompositeKeyFields(o, output);
}
@Override
Object convertRawObject(Catalog catalog,
boolean rawAccess,
RawObject rawObject,
IdentityHashMap converted)
throws RefreshException {
/*
* Synchronization is not required since rawInputFields is immutable.
* If by chance we create duplicate values when two threads execute
* this block, no harm is done. But be sure to assign the field only
* after the values are fully populated.
*/
FieldInfo[] myFields = rawInputFields;
if (myFields == null) {
myFields = new FieldInfo[fields.size()];
fields.toArray(myFields);
rawInputFields = myFields;
}
if (rawObject.getSuper() != null) {
throw new IllegalArgumentException
("RawObject has too many superclasses: " +
rawObject.getType().getClassName());
}
RawObject[] objects = new RawObject[myFields.length];
Arrays.fill(objects, rawObject);
EntityInput in = new RawComplexInput
(catalog, rawAccess, converted, myFields, objects);
Object o = newInstance(in, rawAccess);
converted.put(rawObject, o);
return readObject(o, in, rawAccess);
}
@Override
void skipContents(RecordInput input)
throws RefreshException {
int maxNum = fields.size();
for (int i = 0; i < maxNum; i += 1) {
fields.get(i).getType().skipContents(input);
}
}
@Override
void copySecKey(RecordInput input, RecordOutput output) {
int maxNum = fields.size();
for (int i = 0; i < maxNum; i += 1) {
fields.get(i).getType().copySecKey(input, output);
}
}
@Override
Format getSequenceKeyFormat() {
if (fields.size() != 1) {
throw new IllegalArgumentException
("A composite key class used with a sequence may contain " +
"only a single key field: " + getClassName());
}
return fields.get(0).getType().getSequenceKeyFormat();
}
@Override
boolean evolve(Format newFormatParam, Evolver evolver) {
/* Disallow evolution to a non-composite format. */
if (!(newFormatParam instanceof CompositeKeyFormat)) {
evolver.addEvolveError
(this, newFormatParam, null,
"A composite key class may not be changed to a different " +
"type");
return false;
}
CompositeKeyFormat newFormat = (CompositeKeyFormat) newFormatParam;
/* Check for added or removed key fields. */
if (fields.size() != newFormat.fields.size()) {
evolver.addEvolveError
(this, newFormat,
"Composite key class fields were added or removed ",
"Old fields: " + fields +
" new fields: " + newFormat.fields);
return false;
}
/* Check for modified key fields. */
boolean newVersion = false;
for (int i = 0; i < fields.size(); i += 1) {
int result = evolver.evolveRequiredKeyField
(this, newFormat, fields.get(i),
newFormat.fields.get(i));
if (result == Evolver.EVOLVE_FAILURE) {
return false;
}
if (result == Evolver.EVOLVE_NEEDED) {
newVersion = true;
}
}
/*
* We never need to use a custom reader because the physical key field
* formats never change. But we do create a new evolved format when
* a type changes (primitive <-> primitive wrapper) so that the new
* type information is correct.
*/
if (newVersion) {
evolver.useEvolvedFormat(this, newFormat, newFormat);
} else {
evolver.useOldFormat(this, newFormat);
}
return true;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy