ComplexFormat.java [plain text]
package com.sleepycat.persist.impl;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.sleepycat.persist.evolve.Converter;
import com.sleepycat.persist.evolve.Deleter;
import com.sleepycat.persist.evolve.EntityConverter;
import com.sleepycat.persist.evolve.Mutations;
import com.sleepycat.persist.evolve.Renamer;
import com.sleepycat.persist.model.ClassMetadata;
import com.sleepycat.persist.model.EntityMetadata;
import com.sleepycat.persist.model.FieldMetadata;
import com.sleepycat.persist.model.Relationship;
import com.sleepycat.persist.model.SecondaryKeyMetadata;
import com.sleepycat.persist.raw.RawField;
import com.sleepycat.persist.raw.RawObject;
public class ComplexFormat extends Format {
private static final long serialVersionUID = -2847843033590454917L;
private ClassMetadata clsMeta;
private EntityMetadata entityMeta;
private FieldInfo priKeyField;
private List<FieldInfo> secKeyFields;
private List<FieldInfo> nonKeyFields;
private FieldReader secKeyFieldReader;
private FieldReader nonKeyFieldReader;
private Map<String,String> oldToNewKeyMap;
private Map<String,String> newToOldFieldMap;
private boolean evolveNeeded;
private transient Accessor objAccessor;
private transient Accessor rawAccessor;
private transient Format entityFormat;
private transient Map<String,FieldAddress> secKeyAddresses;
private transient volatile Map<String,RawField> rawFields;
private transient volatile FieldInfo[] rawInputFields;
private transient volatile int[] rawInputLevels;
private transient volatile int rawInputDepth;
ComplexFormat(Class cls,
ClassMetadata clsMeta,
EntityMetadata entityMeta) {
super(cls);
this.clsMeta = clsMeta;
this.entityMeta = entityMeta;
secKeyFields = new ArrayList<FieldInfo>();
nonKeyFields = FieldInfo.getInstanceFields(cls);
if (clsMeta.getPrimaryKey() != null) {
String fieldName = clsMeta.getPrimaryKey().getName();
FieldInfo field = FieldInfo.getField(nonKeyFields, fieldName);
if (field == null) {
throw new IllegalArgumentException
("Primary key field does not exist: " +
getClassName() + '.' + fieldName);
}
nonKeyFields.remove(field);
priKeyField = field;
}
if (clsMeta.getSecondaryKeys() != null) {
for (SecondaryKeyMetadata secKeyMeta :
clsMeta.getSecondaryKeys().values()) {
String fieldName = secKeyMeta.getName();
FieldInfo field = FieldInfo.getField(nonKeyFields, fieldName);
if (field == null) {
throw new IllegalArgumentException
("Secondary key field does not exist: " +
getClassName() + '.' + fieldName);
}
Class fieldCls = field.getFieldClass();
Relationship rel = secKeyMeta.getRelationship();
if (rel == Relationship.ONE_TO_MANY ||
rel == Relationship.MANY_TO_MANY) {
if (!PersistKeyCreator.isManyType(fieldCls)) {
throw new IllegalArgumentException
("ONE_TO_MANY and MANY_TO_MANY keys must" +
" have an array or Collection type: " +
getClassName() + '.' + fieldName);
}
} else {
if (PersistKeyCreator.isManyType(fieldCls)) {
throw new IllegalArgumentException
("ONE_TO_ONE and MANY_TO_ONE keys must not" +
" have an array or Collection type: " +
getClassName() + '.' + fieldName);
}
}
nonKeyFields.remove(field);
secKeyFields.add(field);
}
}
Collections.sort(secKeyFields);
Collections.sort(nonKeyFields);
}
@Override
void migrateFromBeta(Map<String,Format> formatMap) {
super.migrateFromBeta(formatMap);
if (priKeyField != null) {
priKeyField.migrateFromBeta(formatMap);
}
for (FieldInfo field : secKeyFields) {
field.migrateFromBeta(formatMap);
}
for (FieldInfo field : nonKeyFields) {
field.migrateFromBeta(formatMap);
}
}
private ComplexFormat getComplexSuper() {
return (ComplexFormat) getSuperFormat();
}
private ComplexFormat getComplexLatest() {
return (ComplexFormat) getLatestVersion();
}
String getPriKeyField() {
if (clsMeta.getPrimaryKey() != null) {
return clsMeta.getPrimaryKey().getName();
} else {
return null;
}
}
@Override
boolean isEntity() {
return clsMeta.isEntityClass();
}
@Override
boolean isModelClass() {
return true;
}
@Override
ClassMetadata getClassMetadata() {
return clsMeta;
}
@Override
EntityMetadata getEntityMetadata() {
return entityMeta;
}
@Override
Format getEntityFormat() {
if (isInitialized()) {
return entityFormat;
} else {
for (Format format = this;
format != null;
format = format.getSuperFormat()) {
if (format.isEntity()) {
return format;
}
}
return null;
}
}
@Override
void setEvolveNeeded(boolean needed) {
evolveNeeded = needed;
}
@Override
boolean getEvolveNeeded() {
return evolveNeeded;
}
@Override
public Map<String,RawField> getFields() {
if (rawFields == null) {
Map<String,RawField> map = new HashMap<String,RawField>();
if (priKeyField != null) {
map.put(priKeyField.getName(), priKeyField);
}
for (RawField field : secKeyFields) {
map.put(field.getName(), field);
}
for (RawField field : nonKeyFields) {
map.put(field.getName(), field);
}
rawFields = map;
}
return rawFields;
}
@Override
void collectRelatedFormats(Catalog catalog,
Map<String,Format> newFormats) {
Class cls = getType();
if (priKeyField != null) {
priKeyField.collectRelatedFormats(catalog, newFormats);
}
for (FieldInfo field : secKeyFields) {
field.collectRelatedFormats(catalog, newFormats);
}
for (FieldInfo field : nonKeyFields) {
field.collectRelatedFormats(catalog, newFormats);
}
if (entityMeta != null) {
for (SecondaryKeyMetadata secKeyMeta :
entityMeta.getSecondaryKeys().values()) {
String elemClsName = secKeyMeta.getElementClassName();
if (elemClsName != null) {
Class elemCls =
SimpleCatalog.keyClassForName(elemClsName);
catalog.createFormat(elemCls, newFormats);
}
}
}
Class superCls = cls.getSuperclass();
if (superCls != Object.class) {
Format superFormat = catalog.createFormat(superCls, newFormats);
if (!(superFormat instanceof ComplexFormat)) {
throw new IllegalArgumentException
("The superclass of a complex type must not be a" +
" composite key class or a simple type class: " +
superCls.getName());
}
}
String proxiedClsName = clsMeta.getProxiedClassName();
if (proxiedClsName != null) {
catalog.createFormat(proxiedClsName, newFormats);
}
}
@Override
void initialize(Catalog catalog, int initVersion) {
Class type = getType();
boolean useEnhanced = false;
if (type != null) {
useEnhanced = EnhancedAccessor.isEnhanced(type);
}
if (priKeyField != null) {
priKeyField.initialize(catalog, initVersion);
}
for (FieldInfo field : secKeyFields) {
field.initialize(catalog, initVersion);
}
for (FieldInfo field : nonKeyFields) {
field.initialize(catalog, initVersion);
}
ComplexFormat superFormat = getComplexSuper();
if (type != null && superFormat == null) {
Class superCls = type.getSuperclass();
if (superCls != Object.class) {
superFormat =
(ComplexFormat) catalog.getFormat(superCls.getName());
setSuperFormat(superFormat);
}
}
if (superFormat != null) {
superFormat.initializeIfNeeded(catalog);
Accessor superAccessor = superFormat.objAccessor;
if (type != null && superAccessor != null) {
if (useEnhanced) {
if (!(superAccessor instanceof EnhancedAccessor)) {
throw new IllegalStateException
("The superclass of an enhanced class must also " +
"be enhanced: " + getClassName() +
" extends " + superFormat.getClassName());
}
} else {
if (!(superAccessor instanceof ReflectionAccessor)) {
throw new IllegalStateException
("The superclass of an unenhanced class must " +
"not be enhanced: " + getClassName() +
" extends " + superFormat.getClassName());
}
}
}
}
for (Format format = this;
format != null;
format = format.getSuperFormat()) {
if (format.isEntity()) {
entityFormat = format;
break;
}
}
if (entityFormat != null &&
entityFormat != this &&
priKeyField != null) {
throw new IllegalArgumentException
("A PrimaryKey may not appear on an Entity subclass: " +
getClassName() + " field: " + priKeyField.getName());
}
if (type != null) {
if (useEnhanced) {
objAccessor = new EnhancedAccessor(catalog, type, this);
} else {
Accessor superObjAccessor =
(superFormat != null) ? superFormat.objAccessor : null;
objAccessor = new ReflectionAccessor
(catalog, type, superObjAccessor, priKeyField,
secKeyFields, nonKeyFields);
}
}
Accessor superRawAccessor =
(superFormat != null) ? superFormat.rawAccessor : null;
rawAccessor = new RawAccessor
(this, superRawAccessor, priKeyField, secKeyFields, nonKeyFields);
EntityMetadata latestEntityMeta = null;
if (entityFormat != null) {
latestEntityMeta =
entityFormat.getLatestVersion().getEntityMetadata();
}
if (latestEntityMeta != null) {
secKeyAddresses = new HashMap<String,FieldAddress>();
ComplexFormat thisLatest = getComplexLatest();
if (thisLatest != this) {
thisLatest.initializeIfNeeded(catalog);
}
nextKeyLoop:
for (SecondaryKeyMetadata secKeyMeta :
latestEntityMeta.getSecondaryKeys().values()) {
String clsName = secKeyMeta.getDeclaringClassName();
String fieldName = secKeyMeta.getName();
int superLevel = 0;
for (ComplexFormat format = this;
format != null;
format = format.getComplexSuper()) {
if (clsName.equals
(format.getLatestVersion().getClassName())) {
String useFieldName = null;
if (format.newToOldFieldMap != null &&
format.newToOldFieldMap.containsKey(fieldName)) {
useFieldName =
format.newToOldFieldMap.get(fieldName);
} else {
useFieldName = fieldName;
}
boolean isSecField;
int fieldNum;
FieldInfo info = FieldInfo.getField
(format.secKeyFields, useFieldName);
if (info != null) {
isSecField = true;
fieldNum = format.secKeyFields.indexOf(info);
} else {
isSecField = false;
info = FieldInfo.getField
(format.nonKeyFields, useFieldName);
if (info == null) {
assert thisLatest != this;
thisLatest.checkNewSecKeyInitializer
(secKeyMeta);
continue nextKeyLoop;
}
fieldNum = format.nonKeyFields.indexOf(info);
}
FieldAddress addr = new FieldAddress
(isSecField, fieldNum, superLevel, format,
info.getType());
secKeyAddresses.put(secKeyMeta.getKeyName(), addr);
}
superLevel += 1;
}
}
}
}
private void checkNewSecKeyInitializer(SecondaryKeyMetadata secKeyMeta) {
if (objAccessor != null) {
FieldAddress addr = secKeyAddresses.get(secKeyMeta.getKeyName());
Object obj = objAccessor.newInstance();
Object val = objAccessor.getField
(obj, addr.fieldNum, addr.superLevel, addr.isSecField);
if (val != null) {
if (addr.keyFormat.isPrimitive()) {
throw new IllegalArgumentException
("For a new secondary key field the field type must " +
"not be a primitive -- class: " +
secKeyMeta.getDeclaringClassName() + " field: " +
secKeyMeta.getName());
} else {
throw new IllegalArgumentException
("For a new secondary key field the default " +
"constructor must not initialize the field to a " +
"non-null value -- class: " +
secKeyMeta.getDeclaringClassName() + " field: " +
secKeyMeta.getName());
}
}
}
}
private boolean nullOrEqual(Object o1, Object o2) {
if (o1 == null) {
return o2 == null;
} else {
return o1.equals(o2);
}
}
@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) {
Accessor accessor = rawAccess ? rawAccessor : objAccessor;
accessor.readSecKeyFields(o, input, 0, Accessor.MAX_FIELD_NUM, -1);
accessor.readNonKeyFields(o, input, 0, Accessor.MAX_FIELD_NUM, -1);
return o;
}
@Override
void writeObject(Object o, EntityOutput output, boolean rawAccess) {
Accessor accessor = rawAccess ? rawAccessor : objAccessor;
accessor.writeSecKeyFields(o, output);
accessor.writeNonKeyFields(o, output);
}
@Override
Object convertRawObject(Catalog catalog,
boolean rawAccess,
RawObject rawObject,
IdentityHashMap converted) {
FieldInfo[] fields = rawInputFields;
int[] levels = rawInputLevels;
int depth = rawInputDepth;
if (fields == null || levels == null || depth == 0) {
depth = 0;
int nFields = 0;
for (ComplexFormat format = this;
format != null;
format = format.getComplexSuper()) {
nFields += format.getNFields();
depth += 1;
}
ComplexFormat[] hierarchy = new ComplexFormat[depth];
int level = depth;
for (ComplexFormat format = this;
format != null;
format = format.getComplexSuper()) {
level -= 1;
hierarchy[level] = format;
}
assert level == 0;
levels = new int[nFields];
fields = new FieldInfo[nFields];
int index = 0;
if (getEntityFormat() != null) {
for (level = depth - 1; level >= 0; level -= 1) {
ComplexFormat format = hierarchy[level];
if (format.priKeyField != null) {
levels[index] = level;
fields[index] = format.priKeyField;
index += 1;
break;
}
}
assert index == 1;
}
for (level = 0; level < depth; level += 1) {
ComplexFormat format = hierarchy[level];
for (FieldInfo field : format.secKeyFields) {
levels[index] = level;
fields[index] = field;
index += 1;
}
}
for (level = 0; level < depth; level += 1) {
ComplexFormat format = hierarchy[level];
for (FieldInfo field : format.nonKeyFields) {
levels[index] = level;
fields[index] = field;
index += 1;
}
}
assert index == fields.length;
rawInputFields = fields;
rawInputLevels = levels;
rawInputDepth = depth;
}
RawObject[] objectsByLevel = new RawObject[depth];
int level = depth;
for (RawObject raw = rawObject; raw != null; raw = raw.getSuper()) {
if (level == 0) {
throw new IllegalArgumentException
("RawObject has too many superclasses: " +
rawObject.getType().getClassName());
}
level -= 1;
objectsByLevel[level] = raw;
}
if (level > 0) {
throw new IllegalArgumentException
("RawObject has too few superclasses: " +
rawObject.getType().getClassName());
}
assert level == 0;
RawObject[] objects = new RawObject[fields.length];
for (int i = 0; i < objects.length; i += 1) {
objects[i] = objectsByLevel[levels[i]];
}
EntityInput in = new RawComplexInput
(catalog, rawAccess, converted, fields, objects);
Object o = newInstance(in, rawAccess);
converted.put(rawObject, o);
if (getEntityFormat() != null) {
readPriKey(o, in, rawAccess);
}
return readObject(o, in, rawAccess);
}
@Override
boolean isPriKeyNullOrZero(Object o, boolean rawAccess) {
Accessor accessor = rawAccess ? rawAccessor : objAccessor;
return accessor.isPriKeyFieldNullOrZero(o);
}
@Override
void writePriKey(Object o, EntityOutput output, boolean rawAccess) {
Accessor accessor = rawAccess ? rawAccessor : objAccessor;
accessor.writePriKeyField(o, output);
}
@Override
public void readPriKey(Object o, EntityInput input, boolean rawAccess) {
Accessor accessor = rawAccess ? rawAccessor : objAccessor;
accessor.readPriKeyField(o, input);
}
@Override
boolean nullifySecKey(Catalog catalog,
Object entity,
String keyName,
Object keyElement) {
if (secKeyAddresses == null) {
throw new IllegalStateException();
}
FieldAddress addr = secKeyAddresses.get(keyName);
if (addr != null) {
Object oldVal = rawAccessor.getField
(entity, addr.fieldNum, addr.superLevel, addr.isSecField);
if (oldVal != null) {
if (keyElement != null) {
RawObject container = (RawObject) oldVal;
Object[] a1 = container.getElements();
boolean isArray = (a1 != null);
if (!isArray) {
a1 = CollectionProxy.getElements(container);
}
if (a1 != null) {
for (int i = 0; i < a1.length; i += 1) {
if (keyElement.equals(a1[i])) {
int len = a1.length - 1;
Object[] a2 = new Object[len];
System.arraycopy(a1, 0, a2, 0, i);
System.arraycopy(a1, i + 1, a2, i, len - i);
if (isArray) {
rawAccessor.setField
(entity, addr.fieldNum,
addr.superLevel, addr.isSecField,
new RawObject
(container.getType(), a2));
} else {
CollectionProxy.setElements(container, a2);
}
return true;
}
}
}
return false;
} else {
rawAccessor.setField
(entity, addr.fieldNum, addr.superLevel,
addr.isSecField, null);
return true;
}
} else {
return false;
}
} else {
return false;
}
}
@Override
void skipContents(RecordInput input) {
skipToSecKeyField(input, Accessor.MAX_FIELD_NUM);
skipToNonKeyField(input, Accessor.MAX_FIELD_NUM);
}
@Override
void copySecMultiKey(RecordInput input, Format keyFormat, Set results) {
CollectionProxy.copyElements(input, this, keyFormat, results);
}
@Override
Format skipToSecKey(RecordInput input, String keyName) {
if (secKeyAddresses == null) {
throw new IllegalStateException();
}
FieldAddress addr = secKeyAddresses.get(keyName);
if (addr != null) {
if (addr.isSecField) {
addr.clsFormat.skipToSecKeyField(input, addr.fieldNum);
} else {
skipToSecKeyField(input, Accessor.MAX_FIELD_NUM);
addr.clsFormat.skipToNonKeyField(input, addr.fieldNum);
}
return addr.keyFormat;
} else {
return null;
}
}
private int getNFields() {
return ((priKeyField != null) ? 1 : 0) +
secKeyFields.size() +
nonKeyFields.size();
}
private void skipToSecKeyField(RecordInput input, int toFieldNum) {
ComplexFormat superFormat = getComplexSuper();
if (superFormat != null) {
superFormat.skipToSecKeyField(input, Accessor.MAX_FIELD_NUM);
}
int maxNum = Math.min(secKeyFields.size(), toFieldNum);
for (int i = 0; i < maxNum; i += 1) {
input.skipField(secKeyFields.get(i).getType());
}
}
private void skipToNonKeyField(RecordInput input, int toFieldNum) {
ComplexFormat superFormat = getComplexSuper();
if (superFormat != null) {
superFormat.skipToNonKeyField(input, Accessor.MAX_FIELD_NUM);
}
int maxNum = Math.min(nonKeyFields.size(), toFieldNum);
for (int i = 0; i < maxNum; i += 1) {
input.skipField(nonKeyFields.get(i).getType());
}
}
private static class FieldAddress {
boolean isSecField;
int fieldNum;
int superLevel;
ComplexFormat clsFormat;
Format keyFormat;
FieldAddress(boolean isSecField,
int fieldNum,
int superLevel,
ComplexFormat clsFormat,
Format keyFormat) {
this.isSecField = isSecField;
this.fieldNum = fieldNum;
this.superLevel = superLevel;
this.clsFormat = clsFormat;
this.keyFormat = keyFormat;
}
}
@Override
boolean evolve(Format newFormatParam, Evolver evolver) {
if (!(newFormatParam instanceof ComplexFormat)) {
evolver.addMissingMutation
(this, newFormatParam,
"Converter is required when a complex type is changed " +
"to a simple type or enum type");
return false;
}
ComplexFormat newFormat = (ComplexFormat) newFormatParam;
Mutations mutations = evolver.getMutations();
boolean thisChanged = false;
boolean superChanged = false;
Map<String,String> allKeyNameMap = new HashMap<String,String>();
for (ComplexFormat oldSuper = getComplexSuper();
oldSuper != null;
oldSuper = oldSuper.getComplexSuper()) {
Converter converter = mutations.getConverter
(oldSuper.getClassName(), oldSuper.getVersion(), null);
if (converter != null) {
evolver.addMissingMutation
(this, newFormatParam,
"Converter is required for this subclass when a " +
"Converter appears on its superclass: " + converter);
return false;
}
if (!evolver.evolveFormat(oldSuper)) {
return false;
}
if (!oldSuper.isCurrentVersion()) {
if (oldSuper.isDeleted()) {
if (!oldSuper.evolveDeletedClass(evolver)) {
return false;
}
}
if (oldSuper.oldToNewKeyMap != null) {
allKeyNameMap.putAll(oldSuper.oldToNewKeyMap);
}
superChanged = true;
}
}
Class newFormatCls = newFormat.getExistingType();
Class newSuper = newFormatCls;
List<Integer> newLevels = new ArrayList<Integer>();
int newLevel = 0;
newLevels.add(newLevel);
if (getSuperFormat() == null) {
if (newFormatCls.getSuperclass() != Object.class) {
thisChanged = true;
superChanged = true;
}
} else {
if (!getSuperFormat().getLatestVersion().getClassName().equals
(newFormatCls.getSuperclass().getName())) {
thisChanged = true;
superChanged = true;
}
}
for (ComplexFormat oldSuper = getComplexSuper();
oldSuper != null;
oldSuper = oldSuper.getComplexSuper()) {
String oldSuperName = oldSuper.getLatestVersion().getClassName();
Class foundNewSuper = null;
int tryNewLevel = newLevel;
for (Class newSuper2 = newSuper.getSuperclass();
newSuper2 != Object.class;
newSuper2 = newSuper2.getSuperclass()) {
tryNewLevel += 1;
if (oldSuperName.equals(newSuper2.getName())) {
foundNewSuper = newSuper2;
newLevel = tryNewLevel;
break;
}
}
if (foundNewSuper != null) {
for (Class newSuper2 = newSuper.getSuperclass();
newSuper2 != foundNewSuper;
newSuper2 = newSuper2.getSuperclass()) {
superChanged = true;
for (ComplexFormat oldSuper2 = oldSuper.getComplexSuper();
oldSuper2 != null;
oldSuper2 = oldSuper2.getComplexSuper()) {
String oldSuper2Name =
oldSuper2.getLatestVersion().getClassName();
if (oldSuper2Name.equals(newSuper2.getName())) {
evolver.addMissingMutation
(this, newFormatParam,
"Class Converter is required when a " +
"superclass is moved in the class " +
"hierarchy: " + newSuper2.getName());
return false;
}
}
}
newSuper = foundNewSuper;
newLevels.add(newLevel);
} else {
superChanged = true;
if (!oldSuper.isDeleted()) {
ComplexFormat oldSuperLatest =
oldSuper.getComplexLatest();
if (oldSuperLatest.getNFields() != 0) {
evolver.addMissingMutation
(this, newFormatParam,
"When a superclass is removed from the class " +
"hierarchy, the superclass or all of its " +
"persistent fields must be deleted with a " +
"Deleter: " +
oldSuperLatest.getClassName());
return false;
}
}
if (isEntity() && isCurrentVersion()) {
Map<String,SecondaryKeyMetadata> secKeys =
oldSuper.clsMeta.getSecondaryKeys();
for (FieldInfo field : oldSuper.secKeyFields) {
SecondaryKeyMetadata meta =
secKeys.get(field.getName());
assert meta != null;
allKeyNameMap.put(meta.getKeyName(), null);
}
}
newLevels.add(EvolveReader.DO_NOT_READ_ACCESSOR);
}
}
int result = evolveAllFields(newFormat, evolver);
if (result == Evolver.EVOLVE_FAILURE) {
return false;
}
if (result == Evolver.EVOLVE_NEEDED) {
thisChanged = true;
}
if (oldToNewKeyMap != null) {
allKeyNameMap.putAll(oldToNewKeyMap);
}
if (thisChanged &&
!evolver.checkUpdatedVersion
("Changes to the fields or superclass were detected", this,
newFormat)) {
return false;
}
if (allKeyNameMap.size() > 0 && isEntity() && isCurrentVersion()) {
for (Map.Entry<String,String> entry : allKeyNameMap.entrySet()) {
String oldKeyName = entry.getKey();
String newKeyName = entry.getValue();
if (newKeyName != null) {
evolver.renameSecondaryDatabase
(this, newFormat, oldKeyName, newKeyName);
} else {
evolver.deleteSecondaryDatabase(this, oldKeyName);
}
}
}
if (superChanged || thisChanged) {
Reader reader = new EvolveReader(newLevels);
evolver.useEvolvedFormat(this, reader, newFormat);
} else {
evolver.useOldFormat(this, newFormat);
}
return true;
}
@Override
boolean evolveMetadata(Format newFormatParam,
Converter converter,
Evolver evolver) {
assert !isDeleted();
assert isEntity();
assert newFormatParam.isEntity();
ComplexFormat newFormat = (ComplexFormat) newFormatParam;
if (!checkKeyTypeChange
(newFormat, entityMeta.getPrimaryKey(),
newFormat.entityMeta.getPrimaryKey(), "primary key",
evolver)) {
return false;
}
Set<String> deletedKeys;
if (converter instanceof EntityConverter) {
EntityConverter entityConverter = (EntityConverter) converter;
deletedKeys = entityConverter.getDeletedKeys();
} else {
deletedKeys = Collections.emptySet();
}
Map<String,SecondaryKeyMetadata> oldSecondaryKeys =
entityMeta.getSecondaryKeys();
Map<String,SecondaryKeyMetadata> newSecondaryKeys =
newFormat.entityMeta.getSecondaryKeys();
Set<String> insertedKeys =
new HashSet<String>(newSecondaryKeys.keySet());
for (SecondaryKeyMetadata oldMeta : oldSecondaryKeys.values()) {
String keyName = oldMeta.getKeyName();
if (deletedKeys.contains(keyName)) {
if (isCurrentVersion()) {
evolver.deleteSecondaryDatabase(this, keyName);
}
} else {
SecondaryKeyMetadata newMeta = newSecondaryKeys.get(keyName);
if (newMeta == null) {
evolver.addInvalidMutation
(this, newFormat, converter,
"Existing key not found in new entity metadata: " +
keyName);
return false;
}
insertedKeys.remove(keyName);
String keyLabel = "secondary key: " + keyName;
if (!checkKeyTypeChange
(newFormat, oldMeta, newMeta, keyLabel, evolver)) {
return false;
}
if (!checkSecKeyMetadata
(newFormat, oldMeta, newMeta, evolver)) {
return false;
}
}
}
if (!insertedKeys.isEmpty()) {
evolver.addEvolveError
(this, newFormat, "Error",
"New keys " + insertedKeys +
" not allowed when using a Converter with an entity class");
}
return true;
}
private boolean checkSecKeyMetadata(Format newFormat,
SecondaryKeyMetadata oldMeta,
SecondaryKeyMetadata newMeta,
Evolver evolver) {
if (oldMeta.getRelationship() != newMeta.getRelationship()) {
evolver.addEvolveError
(this, newFormat,
"Change detected in the relate attribute (Relationship) " +
"of a secondary key",
"Old key: " + oldMeta.getKeyName() +
" relate: " + oldMeta.getRelationship() +
" new key: " + newMeta.getKeyName() +
" relate: " + newMeta.getRelationship());
return false;
}
return true;
}
private boolean checkKeyTypeChange(Format newFormat,
FieldMetadata oldMeta,
FieldMetadata newMeta,
String keyLabel,
Evolver evolver) {
String oldClass = oldMeta.getClassName();
String newClass = newMeta.getClassName();
if (!oldClass.equals(newClass)) {
SimpleCatalog catalog = SimpleCatalog.getInstance();
Format oldType = catalog.getFormat(oldClass);
Format newType = catalog.getFormat(newClass);
if (oldType == null || newType == null ||
((oldType.getWrapperFormat() == null ||
oldType.getWrapperFormat().getId() !=
newType.getId()) &&
(newType.getWrapperFormat() == null ||
newType.getWrapperFormat().getId() !=
oldType.getId()))) {
evolver.addEvolveError
(this, newFormat,
"Type change detected for " + keyLabel,
"Old field type: " + oldClass +
" is not compatible with the new type: " +
newClass +
" old field: " + oldMeta.getName() +
" new field: " + newMeta.getName());
return false;
}
}
return true;
}
private boolean evolveDeletedClass(Evolver evolver) {
assert isDeleted();
if (secKeyFieldReader == null || nonKeyFieldReader == null) {
if (priKeyField != null &&
getEntityFormat() != null &&
!getEntityFormat().isDeleted()) {
evolver.addEvolveError
(this, this,
"Class containing primary key field was deleted ",
"Primary key is needed in an entity class hierarchy: " +
priKeyField.getName());
return false;
} else {
secKeyFieldReader = new SkipFieldReader(0, secKeyFields);
nonKeyFieldReader = new SkipFieldReader(0, nonKeyFields);
return true;
}
} else {
return true;
}
}
private int evolveAllFields(ComplexFormat newFormat, Evolver evolver) {
assert !isDeleted();
secKeyFieldReader = null;
nonKeyFieldReader = null;
oldToNewKeyMap = null;
boolean evolveFailure = false;
boolean evolveNeeded = false;
if (priKeyField != null) {
int result = evolver.evolveRequiredKeyField
(this, newFormat, priKeyField, newFormat.priKeyField);
if (result == Evolver.EVOLVE_FAILURE) {
evolveFailure = true;
} else if (result == Evolver.EVOLVE_NEEDED) {
evolveNeeded = true;
}
}
FieldReader reader = evolveFieldList
(secKeyFields, newFormat.secKeyFields, true,
newFormat.nonKeyFields, newFormat, evolver);
if (reader == FieldReader.EVOLVE_FAILURE) {
evolveFailure = true;
} else if (reader != null) {
evolveNeeded = true;
}
if (reader != FieldReader.EVOLVE_NEEDED) {
secKeyFieldReader = reader;
}
reader = evolveFieldList
(nonKeyFields, newFormat.nonKeyFields, false,
newFormat.secKeyFields, newFormat, evolver);
if (reader == FieldReader.EVOLVE_FAILURE) {
evolveFailure = true;
} else if (reader != null) {
evolveNeeded = true;
}
if (reader != FieldReader.EVOLVE_NEEDED) {
nonKeyFieldReader = reader;
}
if (evolveFailure) {
return Evolver.EVOLVE_FAILURE;
} else if (evolveNeeded) {
return Evolver.EVOLVE_NEEDED;
} else {
return Evolver.EVOLVE_NONE;
}
}
private FieldReader getDoNothingFieldReader() {
List<FieldReader> emptyList = Collections.emptyList();
return new MultiFieldReader(emptyList);
}
private FieldReader evolveFieldList(List<FieldInfo> oldFields,
List<FieldInfo> newFields,
boolean isOldSecKeyField,
List<FieldInfo> otherNewFields,
ComplexFormat newFormat,
Evolver evolver) {
Mutations mutations = evolver.getMutations();
boolean evolveFailure = false;
boolean evolveNeeded = false;
boolean readerNeeded = false;
List<FieldReader> fieldReaders = new ArrayList<FieldReader>();
FieldReader currentReader = null;
int prevNewFieldIndex = newFields.size();
int newFieldsMatched = 0;
fieldLoop:
for (int oldFieldIndex = 0;
oldFieldIndex < oldFields.size();
oldFieldIndex += 1) {
FieldInfo oldField = oldFields.get(oldFieldIndex);
String oldName = oldField.getName();
SecondaryKeyMetadata oldMeta = null;
if (isOldSecKeyField) {
oldMeta = clsMeta.getSecondaryKeys().get(oldName);
assert oldMeta != null;
}
Renamer renamer = mutations.getRenamer
(getClassName(), getVersion(), oldName);
Deleter deleter = mutations.getDeleter
(getClassName(), getVersion(), oldName);
Converter converter = mutations.getConverter
(getClassName(), getVersion(), oldName);
if (deleter != null && (converter != null || renamer != null)) {
evolver.addInvalidMutation
(this, newFormat, deleter,
"Field Deleter is not allowed along with a Renamer or " +
"Converter for the same field: " + oldName);
evolveFailure = true;
continue fieldLoop;
}
String newName = (renamer != null) ?
renamer.getNewName() : oldName;
if (!oldName.equals(newName)) {
if (newToOldFieldMap == null) {
newToOldFieldMap = new HashMap<String,String>();
}
newToOldFieldMap.put(newName, oldName);
}
int newFieldIndex = FieldInfo.getFieldIndex(newFields, newName);
FieldInfo newField = null;
boolean isNewSecKeyField = isOldSecKeyField;
if (newFieldIndex >= 0) {
newField = newFields.get(newFieldIndex);
} else {
newFieldIndex = FieldInfo.getFieldIndex
(otherNewFields, newName);
if (newFieldIndex >= 0) {
newField = otherNewFields.get(newFieldIndex);
isNewSecKeyField = !isOldSecKeyField;
}
evolveNeeded = true;
readerNeeded = true;
}
if (deleter != null) {
if (newField != null) {
evolver.addInvalidMutation
(this, newFormat, deleter,
"Field Deleter is not allowed when the persistent " +
"field is still present: " + oldName);
evolveFailure = true;
}
if (currentReader instanceof SkipFieldReader &&
currentReader.acceptField
(oldFieldIndex, newFieldIndex, isNewSecKeyField)) {
currentReader.addField(oldField);
} else {
currentReader = new SkipFieldReader
(oldFieldIndex, oldField);
fieldReaders.add(currentReader);
readerNeeded = true;
evolveNeeded = true;
}
if (isOldSecKeyField) {
if (oldToNewKeyMap == null) {
oldToNewKeyMap = new HashMap<String,String>();
}
oldToNewKeyMap.put(oldMeta.getKeyName(), null);
}
continue fieldLoop;
} else {
if (newField == null) {
evolver.addMissingMutation
(this, newFormat,
"Field is not present or not persistent: " +
oldName);
evolveFailure = true;
continue fieldLoop;
}
}
newFieldsMatched += 1;
SecondaryKeyMetadata newMeta = null;
if (isOldSecKeyField && isNewSecKeyField) {
newMeta = newFormat.clsMeta.getSecondaryKeys().get(newName);
assert newMeta != null;
if (!checkSecKeyMetadata
(newFormat, oldMeta, newMeta, evolver)) {
evolveFailure = true;
continue fieldLoop;
}
String oldKeyName = oldMeta.getKeyName();
String newKeyName = newMeta.getKeyName();
if (!oldKeyName.equals(newKeyName)) {
if (oldToNewKeyMap == null) {
oldToNewKeyMap = new HashMap<String,String>();
}
oldToNewKeyMap.put(oldName, newName);
evolveNeeded = true;
}
} else if (isOldSecKeyField && !isNewSecKeyField) {
if (oldToNewKeyMap == null) {
oldToNewKeyMap = new HashMap<String,String>();
}
oldToNewKeyMap.put(oldMeta.getKeyName(), null);
}
if (converter != null) {
if (isOldSecKeyField) {
evolver.addInvalidMutation
(this, newFormat, converter,
"Field Converter is not allowed for secondary key " +
"fields: " + oldName);
evolveFailure = true;
} else {
currentReader = new ConvertFieldReader
(converter, oldFieldIndex, newFieldIndex,
isNewSecKeyField);
fieldReaders.add(currentReader);
readerNeeded = true;
evolveNeeded = true;
}
continue fieldLoop;
}
boolean allClassesConverted = true;
Format oldFieldFormat = oldField.getType();
for (Format formatVersion = oldFieldFormat.getLatestVersion();
true;
formatVersion = formatVersion.getPreviousVersion()) {
assert formatVersion != null;
if (!evolver.evolveFormat(formatVersion)) {
evolveFailure = true;
continue fieldLoop;
}
if (!formatVersion.isNew() &&
!evolver.isClassConverted(formatVersion)) {
allClassesConverted = false;
}
Set<Format> subclassFormats =
evolver.getSubclassFormats(formatVersion);
if (subclassFormats != null) {
for (Format format2 : subclassFormats) {
if (!evolver.evolveFormat(format2)) {
evolveFailure = true;
continue fieldLoop;
}
if (!format2.isNew() &&
!evolver.isClassConverted(format2)) {
allClassesConverted = false;
}
}
}
if (formatVersion == oldFieldFormat) {
break;
}
}
Format oldLatestFormat = oldFieldFormat.getLatestVersion();
Format newFieldFormat = newField.getType();
if (oldLatestFormat.getClassName().equals
(newFieldFormat.getClassName()) &&
!oldLatestFormat.isDeleted()) {
} else if (allClassesConverted) {
evolveNeeded = true;
} else if (WidenerInput.isWideningSupported
(oldLatestFormat, newFieldFormat, isOldSecKeyField)) {
currentReader = new WidenFieldReader
(oldLatestFormat, newFieldFormat, newFieldIndex,
isNewSecKeyField);
fieldReaders.add(currentReader);
readerNeeded = true;
evolveNeeded = true;
continue fieldLoop;
} else {
boolean refWidened = false;
if (!newFieldFormat.isPrimitive() &&
!oldLatestFormat.isPrimitive() &&
!oldLatestFormat.isDeleted() &&
!evolver.isClassConverted(oldLatestFormat)) {
Class oldCls = oldLatestFormat.getExistingType();
Class newCls = newFieldFormat.getExistingType();
if (newCls.isAssignableFrom(oldCls)) {
refWidened = true;
}
}
if (refWidened) {
evolveNeeded = true;
} else {
evolver.addMissingMutation
(this, newFormat,
"Old field type: " + oldLatestFormat.getClassName() +
" is not compatible with the new type: " +
newFieldFormat.getClassName() +
" for field: " + oldName);
evolveFailure = true;
continue fieldLoop;
}
}
if (currentReader instanceof PlainFieldReader &&
currentReader.acceptField
(oldFieldIndex, newFieldIndex, isNewSecKeyField)) {
currentReader.addField(oldField);
} else {
currentReader = new PlainFieldReader
(oldFieldIndex, newFieldIndex, isNewSecKeyField);
fieldReaders.add(currentReader);
}
}
if (newFieldsMatched < newFields.size()) {
evolveNeeded = true;
readerNeeded = true;
}
if (evolveFailure) {
return FieldReader.EVOLVE_FAILURE;
} else if (readerNeeded) {
if (fieldReaders.size() == 0) {
return getDoNothingFieldReader();
} else if (fieldReaders.size() == 1) {
return fieldReaders.get(0);
} else {
return new MultiFieldReader(fieldReaders);
}
} else if (evolveNeeded) {
return FieldReader.EVOLVE_NEEDED;
} else {
return null;
}
}
private static abstract class FieldReader implements Serializable {
static final FieldReader EVOLVE_NEEDED =
new PlainFieldReader(0, 0, false);
static final FieldReader EVOLVE_FAILURE =
new PlainFieldReader(0, 0, false);
private static final long serialVersionUID = 866041475399255164L;
FieldReader() {
}
void initialize(Catalog catalog,
int initVersion,
ComplexFormat oldParentFormat,
ComplexFormat newParentFormat,
boolean isOldSecKey) {
}
boolean acceptField(int oldFieldIndex,
int newFieldIndex,
boolean isNewSecKeyField) {
return false;
}
void addField(FieldInfo oldField) {
throw new UnsupportedOperationException();
}
abstract void readFields(Object o,
EntityInput input,
Accessor accessor,
int superLevel);
}
private static class PlainFieldReader extends FieldReader {
private static final long serialVersionUID = 1795593463439931402L;
private int startField;
private int endField;
private boolean secKeyField;
private transient int endOldField;
PlainFieldReader(int oldFieldIndex,
int newFieldIndex,
boolean isNewSecKeyField) {
endOldField = oldFieldIndex;
startField = newFieldIndex;
endField = newFieldIndex;
secKeyField = isNewSecKeyField;
}
@Override
boolean acceptField(int oldFieldIndex,
int newFieldIndex,
boolean isNewSecKeyField) {
return oldFieldIndex == endOldField + 1 &&
newFieldIndex == endField + 1 &&
secKeyField == isNewSecKeyField;
}
@Override
void addField(FieldInfo oldField) {
endField += 1;
endOldField += 1;
}
@Override
final void readFields(Object o,
EntityInput input,
Accessor accessor,
int superLevel) {
if (secKeyField) {
accessor.readSecKeyFields
(o, input, startField, endField, superLevel);
} else {
accessor.readNonKeyFields
(o, input, startField, endField, superLevel);
}
}
}
private static class SkipFieldReader extends FieldReader {
private static final long serialVersionUID = -3060281692155253098L;
private List<Format> fieldFormats;
private transient int endField;
SkipFieldReader(int startField, List<FieldInfo> fields) {
endField = startField + fields.size() - 1;
fieldFormats = new ArrayList<Format>(fields.size());
for (FieldInfo field : fields) {
fieldFormats.add(field.getType());
}
}
SkipFieldReader(int startField, FieldInfo oldField) {
endField = startField;
fieldFormats = new ArrayList<Format>();
fieldFormats.add(oldField.getType());
}
@Override
boolean acceptField(int oldFieldIndex,
int newFieldIndex,
boolean isNewSecKeyField) {
return oldFieldIndex == endField + 1;
}
@Override
void addField(FieldInfo oldField) {
endField += 1;
fieldFormats.add(oldField.getType());
}
@Override
final void readFields(Object o,
EntityInput input,
Accessor accessor,
int superLevel) {
for (Format format : fieldFormats) {
input.skipField(format);
}
}
}
private static class ConvertFieldReader extends FieldReader {
private static final long serialVersionUID = 8736410481633998710L;
private Converter converter;
private int oldFieldNum;
private int fieldNum;
private boolean secKeyField;
private transient Format oldFormat;
private transient Format newFormat;
ConvertFieldReader(Converter converter,
int oldFieldIndex,
int newFieldIndex,
boolean isNewSecKeyField) {
this.converter = converter;
oldFieldNum = oldFieldIndex;
fieldNum = newFieldIndex;
secKeyField = isNewSecKeyField;
}
@Override
void initialize(Catalog catalog,
int initVersion,
ComplexFormat oldParentFormat,
ComplexFormat newParentFormat,
boolean isOldSecKey) {
if (initVersion < 1) {
oldFieldNum = fieldNum;
}
if (isOldSecKey) {
oldFormat =
oldParentFormat.secKeyFields.get(oldFieldNum).getType();
} else {
oldFormat =
oldParentFormat.nonKeyFields.get(oldFieldNum).getType();
}
if (secKeyField) {
newFormat =
newParentFormat.secKeyFields.get(fieldNum).getType();
} else {
newFormat =
newParentFormat.nonKeyFields.get(fieldNum).getType();
}
}
@Override
final void readFields(Object o,
EntityInput input,
Accessor accessor,
int superLevel) {
boolean currentRawMode = input.setRawAccess(true);
Object value;
try {
if (oldFormat.isPrimitive()) {
value = input.readKeyObject(oldFormat);
} else {
value = input.readObject();
}
} finally {
input.setRawAccess(currentRawMode);
}
Catalog catalog = input.getCatalog();
value = converter.getConversion().convert(value);
EntityInput rawInput = new RawSingleInput
(catalog, currentRawMode, null, value, newFormat);
if (secKeyField) {
accessor.readSecKeyFields
(o, rawInput, fieldNum, fieldNum, superLevel);
} else {
accessor.readNonKeyFields
(o, rawInput, fieldNum, fieldNum, superLevel);
}
}
}
private static class WidenFieldReader extends FieldReader {
private static final long serialVersionUID = -2054520670170407282L;
private int fromFormatId;
private int toFormatId;
private int fieldNum;
private boolean secKeyField;
WidenFieldReader(Format oldFormat,
Format newFormat,
int newFieldIndex,
boolean isNewSecKeyField) {
fromFormatId = oldFormat.getId();
toFormatId = newFormat.getId();
fieldNum = newFieldIndex;
secKeyField = isNewSecKeyField;
}
@Override
final void readFields(Object o,
EntityInput input,
Accessor accessor,
int superLevel) {
EntityInput widenerInput = new WidenerInput
(input, fromFormatId, toFormatId);
if (secKeyField) {
accessor.readSecKeyFields
(o, widenerInput, fieldNum, fieldNum, superLevel);
} else {
accessor.readNonKeyFields
(o, widenerInput, fieldNum, fieldNum, superLevel);
}
}
}
private static class MultiFieldReader extends FieldReader {
private static final long serialVersionUID = -6035976787562441473L;
private List<FieldReader> subReaders;
MultiFieldReader(List<FieldReader> subReaders) {
this.subReaders = subReaders;
}
@Override
void initialize(Catalog catalog,
int initVersion,
ComplexFormat oldParentFormat,
ComplexFormat newParentFormat,
boolean isOldSecKey) {
for (FieldReader reader : subReaders) {
reader.initialize
(catalog, initVersion, oldParentFormat, newParentFormat,
isOldSecKey);
}
}
@Override
final void readFields(Object o,
EntityInput input,
Accessor accessor,
int superLevel) {
for (FieldReader reader : subReaders) {
reader.readFields(o, input, accessor, superLevel);
}
}
}
private static class EvolveReader implements Reader {
static final int DO_NOT_READ_ACCESSOR = Integer.MAX_VALUE;
private static final long serialVersionUID = -1016140948306913283L;
private transient ComplexFormat newFormat;
private transient ComplexFormat[] oldHierarchy;
private int[] newHierarchyLevels;
EvolveReader(List<Integer> newHierarchyLevelsList) {
int oldDepth = newHierarchyLevelsList.size();
newHierarchyLevels = new int[oldDepth];
newHierarchyLevelsList.toArray();
for (int i = 0; i < oldDepth; i += 1) {
newHierarchyLevels[i] = newHierarchyLevelsList.get(i);
}
}
public void initializeReader(Catalog catalog,
int initVersion,
Format oldFormatParam) {
ComplexFormat oldFormat = (ComplexFormat) oldFormatParam;
newFormat = oldFormat.getComplexLatest();
newFormat.initializeIfNeeded(catalog);
int newDepth = 0;
for (Format format = newFormat;
format != null;
format = format.getSuperFormat()) {
newDepth += 1;
}
ComplexFormat[] newHierarchy = new ComplexFormat[newDepth];
int level = 0;
for (ComplexFormat format = newFormat;
format != null;
format = format.getComplexSuper()) {
newHierarchy[level] = format;
level += 1;
}
assert level == newDepth;
int oldDepth = newHierarchyLevels.length;
oldHierarchy = new ComplexFormat[oldDepth];
level = 0;
for (ComplexFormat oldFormat2 = oldFormat;
oldFormat2 != null;
oldFormat2 = oldFormat2.getComplexSuper()) {
oldHierarchy[level] = oldFormat2;
int level2 = newHierarchyLevels[level];
ComplexFormat newFormat2 = (level2 != DO_NOT_READ_ACCESSOR) ?
newHierarchy[level2] : null;
level += 1;
if (oldFormat2.secKeyFieldReader != null) {
oldFormat2.secKeyFieldReader.initialize
(catalog, initVersion, oldFormat2, newFormat2, true);
}
if (oldFormat2.nonKeyFieldReader != null) {
oldFormat2.nonKeyFieldReader.initialize
(catalog, initVersion, oldFormat2, newFormat2, false);
}
}
assert level == oldDepth;
}
public Object newInstance(EntityInput input, boolean rawAccess) {
return newFormat.newInstance(input, rawAccess);
}
public void readPriKey(Object o,
EntityInput input,
boolean rawAccess) {
newFormat.readPriKey(o, input, rawAccess);
}
public Object readObject(Object o,
EntityInput input,
boolean rawAccess) {
Accessor accessor = rawAccess ? newFormat.rawAccessor
: newFormat.objAccessor;
int maxMinusOne = oldHierarchy.length - 1;
for (int i = maxMinusOne; i >= 0; i -= 1) {
FieldReader reader = oldHierarchy[i].secKeyFieldReader;
int newLevel = newHierarchyLevels[i];
if (reader != null) {
reader.readFields(o, input, accessor, newLevel);
} else if (newLevel != DO_NOT_READ_ACCESSOR) {
accessor.readSecKeyFields
(o, input, 0, Accessor.MAX_FIELD_NUM, newLevel);
}
}
for (int i = maxMinusOne; i >= 0; i -= 1) {
FieldReader reader = oldHierarchy[i].nonKeyFieldReader;
int newLevel = newHierarchyLevels[i];
if (reader != null) {
reader.readFields(o, input, accessor, newLevel);
} else if (newLevel != DO_NOT_READ_ACCESSOR) {
accessor.readNonKeyFields
(o, input, 0, Accessor.MAX_FIELD_NUM, newLevel);
}
}
return o;
}
}
}