com.sleepycat.persist.impl.EnumFormat 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.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.sleepycat.compat.DbCompat;
import com.sleepycat.persist.model.EntityModel;
import com.sleepycat.persist.raw.RawObject;
/**
* Format for all enum types.
*
* In this class we resort to using reflection to allocate arrays of enums.
* If there is a need for it, reflection could be avoided in the future by
* generating code as new array formats are encountered.
*
* @author Mark Hayes
*/
public class EnumFormat extends Format {
private static final long serialVersionUID = 1069833955604373538L;
private String[] names;
private transient Object[] values;
EnumFormat(Catalog catalog, Class type) {
super(catalog, type);
values = type.getEnumConstants();
names = new String[values.length];
for (int i = 0; i < names.length; i += 1) {
names[i] = ((Enum) values[i]).name();
}
}
/**
* For use in a deserialized CompositeKeyFormat.
*/
EnumFormat(Catalog catalog, Class type, String[] enumData) {
super(catalog, type);
names = enumData;
}
/**
* Returns data needed for serialization of a CompositeKeyFormat.
*/
String[] getFormatData() {
return names;
}
@Override
public boolean isEnum() {
return true;
}
@Override
public List getEnumConstants() {
return Arrays.asList(names);
}
@Override
void collectRelatedFormats(Catalog catalog,
Map newFormats) {
}
@Override
void initialize(Catalog catalog, EntityModel model, int initVersion) {
if (values == null) {
initValues();
}
}
private void initValues() {
Class cls = getType();
if (cls != null) {
values = new Object[names.length];
for (int i = 0; i < names.length; i += 1) {
try {
values[i] = Enum.valueOf(cls, names[i]);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException
("Deletion and renaming of enum values is not " +
"supported: " + names[i], e);
}
}
}
}
@Override
Object newArray(int len) {
return Array.newInstance(getType(), len);
}
@Override
public Object newInstance(EntityInput input, boolean rawAccess) {
int index = input.readEnumConstant(names);
if (rawAccess) {
return new RawObject(this, names[index]);
} else {
return values[index];
}
}
@Override
public Object readObject(Object o, EntityInput input, boolean rawAccess) {
/* newInstance reads the value -- do nothing here. */
return o;
}
@Override
void writeObject(Object o, EntityOutput output, boolean rawAccess) {
if (rawAccess) {
String name = ((RawObject) o).getEnum();
for (int i = 0; i < names.length; i += 1) {
if (names[i].equals(name)) {
output.writeEnumConstant(names, i);
return;
}
}
} else {
for (int i = 0; i < values.length; i += 1) {
if (o == values[i]) {
output.writeEnumConstant(names, i);
return;
}
}
}
throw DbCompat.unexpectedState("Bad enum: " + o);
}
@Override
Object convertRawObject(Catalog catalog,
boolean rawAccess,
RawObject rawObject,
IdentityHashMap converted) {
String name = rawObject.getEnum();
for (int i = 0; i < names.length; i += 1) {
if (names[i].equals(name)) {
Object o = values[i];
converted.put(rawObject, o);
return o;
}
}
throw new IllegalArgumentException
("Enum constant is not defined: " + name);
}
@Override
void skipContents(RecordInput input) {
input.skipFast(input.getPackedIntByteLength());
}
@Override
void copySecKey(RecordInput input, RecordOutput output) {
int len = input.getPackedIntByteLength();
output.writeFast
(input.getBufferBytes(), input.getBufferOffset(), len);
input.skipFast(len);
}
@Override
boolean evolve(Format newFormatParam, Evolver evolver) {
if (!(newFormatParam instanceof EnumFormat)) {
evolver.addEvolveError
(this, newFormatParam,
"Incompatible enum type changed detected",
"An enum class may not be changed to a non-enum type");
/* For future:
evolver.addMissingMutation
(this, newFormatParam,
"Converter is required when an enum class is changed to " +
"a non-enum type");
*/
return false;
}
final EnumFormat newFormat = (EnumFormat) newFormatParam;
/* Return quickly if the enum was not changed at all. */
if (Arrays.equals(names, newFormat.names)) {
evolver.useOldFormat(this, newFormat);
return true;
}
final List newNamesList = Arrays.asList(newFormat.names);
final Set newNamesSet = new HashSet(newNamesList);
final List oldNamesList = Arrays.asList(names);
/* Deletion (or renaming, which appears as deletion) is not allowed. */
if (!newNamesSet.containsAll(oldNamesList)) {
final Set oldNamesSet = new HashSet(oldNamesList);
oldNamesSet.removeAll(newNamesSet);
evolver.addEvolveError
(this, newFormat,
"Incompatible enum type changed detected",
"Enum values may not be removed: " + oldNamesSet);
}
/* Use a List for additional names to preserve ordinal order. */
final List additionalNamesList =
new ArrayList(newNamesList);
additionalNamesList.removeAll(oldNamesList);
final int nAdditionalNames = additionalNamesList.size();
/*
* If there are no aditional names, the new and old formats are
* equivalent. This is the case where only the declaration order was
* changed.
*/
if (nAdditionalNames == 0) {
evolver.useOldFormat(this, newFormat);
return true;
}
/*
* Evolve the new format. It should use the old names array, but with
* any additional names appended. [#17140]
*/
final int nOldNames = names.length;
newFormat.names = new String[nOldNames + nAdditionalNames];
System.arraycopy(names, 0, newFormat.names, 0, nOldNames);
for (int i = 0; i < nAdditionalNames; i += 1) {
newFormat.names[nOldNames + i] = additionalNamesList.get(i);
}
newFormat.initValues();
/*
* Because we never change the array index (stored integer value) for
* an enum value, the new format can read the values written by the old
* format (newFormat is used as the Reader in the 2nd param below).
*/
evolver.useEvolvedFormat(this, newFormat, newFormat);
return true;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy