1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002,2008 Oracle. All rights reserved. 5 * 6 * $Id: EnumFormat.java,v 1.1 2008/02/07 17:12:27 mark Exp $ 7 */ 8 9package com.sleepycat.persist.impl; 10 11import java.lang.reflect.Array; 12import java.util.Arrays; 13import java.util.HashSet; 14import java.util.IdentityHashMap; 15import java.util.List; 16import java.util.Map; 17import java.util.Set; 18 19import com.sleepycat.persist.raw.RawObject; 20 21/** 22 * Format for all enum types. 23 * 24 * In this class we resort to using reflection to allocate arrays of enums. 25 * If there is a need for it, reflection could be avoided in the future by 26 * generating code as new array formats are encountered. 27 * 28 * @author Mark Hayes 29 */ 30public class EnumFormat extends Format { 31 32 private static final long serialVersionUID = 1069833955604373538L; 33 34 private String[] names; 35 private transient Object[] values; 36 37 EnumFormat(Class type) { 38 super(type); 39 values = type.getEnumConstants(); 40 names = new String[values.length]; 41 for (int i = 0; i < names.length; i += 1) { 42 names[i] = ((Enum) values[i]).name(); 43 } 44 } 45 46 @Override 47 public boolean isEnum() { 48 return true; 49 } 50 51 @Override 52 public List<String> getEnumConstants() { 53 return Arrays.asList(names); 54 } 55 56 @Override 57 void collectRelatedFormats(Catalog catalog, 58 Map<String,Format> newFormats) { 59 } 60 61 @Override 62 void initialize(Catalog catalog, int initVersion) { 63 if (values == null) { 64 Class cls = getType(); 65 if (cls != null) { 66 values = new Object[names.length]; 67 for (int i = 0; i < names.length; i += 1) { 68 values[i] = Enum.valueOf(cls, names[i]); 69 } 70 } 71 } 72 } 73 74 @Override 75 Object newArray(int len) { 76 return Array.newInstance(getType(), len); 77 } 78 79 @Override 80 public Object newInstance(EntityInput input, boolean rawAccess) { 81 int index = input.readEnumConstant(names); 82 if (rawAccess) { 83 return new RawObject(this, names[index]); 84 } else { 85 return values[index]; 86 } 87 } 88 89 @Override 90 public Object readObject(Object o, EntityInput input, boolean rawAccess) { 91 /* newInstance reads the value -- do nothing here. */ 92 return o; 93 } 94 95 @Override 96 void writeObject(Object o, EntityOutput output, boolean rawAccess) { 97 if (rawAccess) { 98 String name = ((RawObject) o).getEnum(); 99 for (int i = 0; i < names.length; i += 1) { 100 if (names[i].equals(name)) { 101 output.writeEnumConstant(names, i); 102 return; 103 } 104 } 105 } else { 106 for (int i = 0; i < values.length; i += 1) { 107 if (o == values[i]) { 108 output.writeEnumConstant(names, i); 109 return; 110 } 111 } 112 } 113 throw new IllegalStateException("Bad enum: " + o); 114 } 115 116 @Override 117 Object convertRawObject(Catalog catalog, 118 boolean rawAccess, 119 RawObject rawObject, 120 IdentityHashMap converted) { 121 String name = rawObject.getEnum(); 122 for (int i = 0; i < names.length; i += 1) { 123 if (names[i].equals(name)) { 124 Object o = values[i]; 125 converted.put(rawObject, o); 126 return o; 127 } 128 } 129 throw new IllegalArgumentException 130 ("Enum constant is not defined: " + name); 131 } 132 133 @Override 134 void skipContents(RecordInput input) { 135 input.skipFast(input.getPackedIntByteLength()); 136 } 137 138 @Override 139 boolean evolve(Format newFormatParam, Evolver evolver) { 140 if (!(newFormatParam instanceof EnumFormat)) { 141 evolver.addEvolveError 142 (this, newFormatParam, 143 "Incompatible enum type changed detected", 144 "An enum class may not be changed to a non-enum type"); 145 /* For future: 146 evolver.addMissingMutation 147 (this, newFormatParam, 148 "Converter is required when an enum class is changed to " + 149 "a non-enum type"); 150 */ 151 return false; 152 } 153 EnumFormat newFormat = (EnumFormat) newFormatParam; 154 if (Arrays.equals(names, newFormat.names)) { 155 evolver.useOldFormat(this, newFormat); 156 return true; 157 } else { 158 Set<String> oldNames = new HashSet<String>(Arrays.asList(names)); 159 List<String> newNames = Arrays.asList(newFormat.names); 160 if (newNames.containsAll(oldNames)) { 161 evolver.useEvolvedFormat(this, newFormat, newFormat); 162 return true; 163 } else { 164 oldNames.removeAll(newNames); 165 evolver.addEvolveError 166 (this, newFormat, 167 "Incompatible enum type changed detected", 168 "Enum values may not be removed: " + oldNames); 169 /* For future: 170 evolver.addMissingMutation 171 (this, newFormatParam, 172 "Converter is required when a value is removed from an " + 173 "enum: " + oldNames); 174 */ 175 return false; 176 } 177 } 178 } 179} 180