/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2002,2008 Oracle. All rights reserved. * * $Id: EnumFormat.java,v 1.1 2008/02/07 17:12:27 mark Exp $ */ package com.sleepycat.persist.impl; import java.lang.reflect.Array; 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.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(Class type) { super(type); values = type.getEnumConstants(); names = new String[values.length]; for (int i = 0; i < names.length; i += 1) { names[i] = ((Enum) values[i]).name(); } } @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, int initVersion) { if (values == null) { Class cls = getType(); if (cls != null) { values = new Object[names.length]; for (int i = 0; i < names.length; i += 1) { values[i] = Enum.valueOf(cls, names[i]); } } } } @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 new IllegalStateException("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 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; } EnumFormat newFormat = (EnumFormat) newFormatParam; if (Arrays.equals(names, newFormat.names)) { evolver.useOldFormat(this, newFormat); return true; } else { Set oldNames = new HashSet(Arrays.asList(names)); List newNames = Arrays.asList(newFormat.names); if (newNames.containsAll(oldNames)) { evolver.useEvolvedFormat(this, newFormat, newFormat); return true; } else { oldNames.removeAll(newNames); evolver.addEvolveError (this, newFormat, "Incompatible enum type changed detected", "Enum values may not be removed: " + oldNames); /* For future: evolver.addMissingMutation (this, newFormatParam, "Converter is required when a value is removed from an " + "enum: " + oldNames); */ return false; } } } }