001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: CompositeKeyFormat.java,v 1.21.2.5 2008/01/07 15:14:19 cwl Exp $
007: */
008:
009: package com.sleepycat.persist.impl;
010:
011: import java.util.ArrayList;
012: import java.util.Arrays;
013: import java.util.HashMap;
014: import java.util.IdentityHashMap;
015: import java.util.List;
016: import java.util.Map;
017:
018: import com.sleepycat.persist.model.ClassMetadata;
019: import com.sleepycat.persist.model.FieldMetadata;
020: import com.sleepycat.persist.raw.RawField;
021: import com.sleepycat.persist.raw.RawObject;
022:
023: /**
024: * Format for a composite key class.
025: *
026: * This class is similar to ComplexFormat in that a composite key class and
027: * other complex classes have fields, and the Accessor interface is used to
028: * access those fields. Composite key classes are different in the following
029: * ways:
030: *
031: * - The superclass must be Object. No inheritance is allowed.
032: *
033: * - All instance fields must be annotated with @KeyField, which determines
034: * their order in the data bytes.
035: *
036: * - Although fields may be reference types (primitive wrappers or other simple
037: * reference types), they are stored as if they were primitives. No object
038: * format ID is stored, and the class of the object must be the declared
039: * classs of the field; i.e., no polymorphism is allowed for key fields.
040: * In other words, a composite key is stored as an ordinary tuple as defined
041: * in the com.sleepycat.bind.tuple package. This keeps the key small and
042: * gives it a well defined sort order.
043: *
044: * - If the key class implements Comparable, it is called by the Database
045: * btree comparator. It must therefore be available during JE recovery,
046: * before the store and catalog have been opened. To support this, this
047: * format can be constructed during recovery. A SimpleCatalog singleton
048: * instance is used to provide a catalog of simple types that is used by
049: * the composite key format.
050: *
051: * - When interacting with the Accessor, the composite key format treats the
052: * Accessor's non-key fields as its key fields. The Accessor's key fields
053: * are secondary keys, while the composite format's key fields are the
054: * component parts of a single key.
055: *
056: * @author Mark Hayes
057: */
058: public class CompositeKeyFormat extends Format {
059:
060: private static final long serialVersionUID = 306843428409314630L;
061:
062: private ClassMetadata metadata;
063: private List<FieldInfo> fields;
064: private transient Accessor objAccessor;
065: private transient Accessor rawAccessor;
066: private transient volatile Map<String, RawField> rawFields;
067: private transient volatile FieldInfo[] rawInputFields;
068:
069: static String[] getFieldNameArray(List<FieldMetadata> list) {
070: int index = 0;
071: String[] a = new String[list.size()];
072: for (FieldMetadata f : list) {
073: a[index] = f.getName();
074: index += 1;
075: }
076: return a;
077: }
078:
079: CompositeKeyFormat(Class cls, ClassMetadata metadata,
080: List<FieldMetadata> fieldNames) {
081: this (cls, metadata, getFieldNameArray(fieldNames));
082: }
083:
084: CompositeKeyFormat(Class cls, ClassMetadata metadata,
085: String[] fieldNames) {
086: super (cls);
087: this .metadata = metadata;
088:
089: /* Check that the superclass is Object. */
090: Class super Cls = cls.getSuperclass();
091: if (super Cls != Object.class) {
092: throw new IllegalArgumentException(
093: "Composite key class must be derived from Object: "
094: + cls.getName());
095: }
096:
097: /* Populate fields list in fieldNames order. */
098: List<FieldInfo> instanceFields = FieldInfo
099: .getInstanceFields(cls);
100: fields = new ArrayList<FieldInfo>(instanceFields.size());
101: for (String fieldName : fieldNames) {
102: FieldInfo field = null;
103: for (FieldInfo tryField : instanceFields) {
104: if (fieldName.equals(tryField.getName())) {
105: field = tryField;
106: break;
107: }
108: }
109: if (field == null) {
110: throw new IllegalArgumentException(
111: "Composite key field is not an instance field:"
112: + getClassName() + '.' + fieldName);
113: }
114: fields.add(field);
115: instanceFields.remove(field);
116: if (!SimpleCatalog.isSimpleType(field.getFieldClass())) {
117: throw new IllegalArgumentException(
118: "Composite key field is not a simple type: "
119: + getClassName() + '.' + fieldName);
120: }
121: }
122: if (instanceFields.size() > 0) {
123: throw new IllegalArgumentException(
124: "All composite key instance fields must be key fields: "
125: + getClassName() + '.'
126: + instanceFields.get(0).getName());
127: }
128: }
129:
130: @Override
131: void migrateFromBeta(Map<String, Format> formatMap) {
132: super .migrateFromBeta(formatMap);
133: for (FieldInfo field : fields) {
134: field.migrateFromBeta(formatMap);
135: }
136: }
137:
138: @Override
139: boolean isModelClass() {
140: return true;
141: }
142:
143: @Override
144: ClassMetadata getClassMetadata() {
145: if (metadata == null) {
146: throw new IllegalStateException(getClassName());
147: }
148: return metadata;
149: }
150:
151: @Override
152: public Map<String, RawField> getFields() {
153:
154: /*
155: * Lazily create the raw type information. Synchronization is not
156: * required since this object is immutable. If by chance we create two
157: * maps when two threads execute this block, no harm is done. But be
158: * sure to assign the rawFields field only after the map is fully
159: * populated.
160: */
161: if (rawFields == null) {
162: Map<String, RawField> map = new HashMap<String, RawField>();
163: for (RawField field : fields) {
164: map.put(field.getName(), field);
165: }
166: rawFields = map;
167: }
168: return rawFields;
169: }
170:
171: @Override
172: void collectRelatedFormats(Catalog catalog,
173: Map<String, Format> newFormats) {
174: /* Collect field formats. */
175: for (FieldInfo field : fields) {
176: field.collectRelatedFormats(catalog, newFormats);
177: }
178: }
179:
180: @Override
181: void initialize(Catalog catalog, int initVersion) {
182: /* Initialize all fields. */
183: for (FieldInfo field : fields) {
184: field.initialize(catalog, initVersion);
185: }
186: /* Create the accessor. */
187: Class type = getType();
188: if (type != null) {
189: if (EnhancedAccessor.isEnhanced(type)) {
190: objAccessor = new EnhancedAccessor(type);
191: } else {
192: objAccessor = new ReflectionAccessor(catalog, type,
193: fields);
194: }
195: }
196: rawAccessor = new RawAccessor(this , fields);
197: }
198:
199: @Override
200: Object newArray(int len) {
201: return objAccessor.newArray(len);
202: }
203:
204: @Override
205: public Object newInstance(EntityInput input, boolean rawAccess) {
206: Accessor accessor = rawAccess ? rawAccessor : objAccessor;
207: return accessor.newInstance();
208: }
209:
210: @Override
211: public Object readObject(Object o, EntityInput input,
212: boolean rawAccess) {
213: Accessor accessor = rawAccess ? rawAccessor : objAccessor;
214: accessor.readNonKeyFields(o, input, 0, Accessor.MAX_FIELD_NUM,
215: -1);
216: return o;
217: }
218:
219: @Override
220: void writeObject(Object o, EntityOutput output, boolean rawAccess) {
221: Accessor accessor = rawAccess ? rawAccessor : objAccessor;
222: accessor.writeNonKeyFields(o, output);
223: }
224:
225: @Override
226: Object convertRawObject(Catalog catalog, boolean rawAccess,
227: RawObject rawObject, IdentityHashMap converted) {
228:
229: /*
230: * Synchronization is not required since rawInputFields is immutable.
231: * If by chance we create duplicate values when two threads execute
232: * this block, no harm is done. But be sure to assign the field only
233: * after the values are fully populated.
234: */
235: FieldInfo[] myFields = rawInputFields;
236: if (myFields == null) {
237: myFields = new FieldInfo[fields.size()];
238: fields.toArray(myFields);
239: rawInputFields = myFields;
240: }
241: if (rawObject.getSuper() != null) {
242: throw new IllegalArgumentException(
243: "RawObject has too many superclasses: "
244: + rawObject.getType().getClassName());
245: }
246: RawObject[] objects = new RawObject[myFields.length];
247: Arrays.fill(objects, rawObject);
248: EntityInput in = new RawComplexInput(catalog, rawAccess,
249: converted, myFields, objects);
250: Object o = newInstance(in, rawAccess);
251: converted.put(rawObject, o);
252: return readObject(o, in, rawAccess);
253: }
254:
255: @Override
256: void skipContents(RecordInput input) {
257: int maxNum = fields.size();
258: for (int i = 0; i < maxNum; i += 1) {
259: fields.get(i).getType().skipContents(input);
260: }
261: }
262:
263: @Override
264: void copySecKey(RecordInput input, RecordOutput output) {
265: int maxNum = fields.size();
266: for (int i = 0; i < maxNum; i += 1) {
267: fields.get(i).getType().copySecKey(input, output);
268: }
269: }
270:
271: @Override
272: Format getSequenceKeyFormat() {
273: if (fields.size() != 1) {
274: throw new IllegalArgumentException(
275: "A composite key class used with a sequence may contain "
276: + "only a single integer key field: "
277: + getClassName());
278: }
279: return fields.get(0).getType().getSequenceKeyFormat();
280: }
281:
282: @Override
283: boolean evolve(Format newFormatParam, Evolver evolver) {
284:
285: /* Disallow evolution to a non-composite format. */
286: if (!(newFormatParam instanceof CompositeKeyFormat)) {
287: evolver.addEvolveError(this , newFormatParam, null,
288: "A composite key class may not be changed to a different "
289: + "type");
290: return false;
291: }
292: CompositeKeyFormat newFormat = (CompositeKeyFormat) newFormatParam;
293:
294: /* Check for added or removed key fields. */
295: if (fields.size() != newFormat.fields.size()) {
296: evolver
297: .addEvolveError(
298: this ,
299: newFormat,
300: "Composite key class fields were added or removed ",
301: "Old fields: " + fields + " new fields: "
302: + newFormat.fields);
303: return false;
304: }
305:
306: /* Check for modified key fields. */
307: boolean newVersion = false;
308: for (int i = 0; i < fields.size(); i += 1) {
309: int result = evolver.evolveRequiredKeyField(this ,
310: newFormat, fields.get(i), newFormat.fields.get(i));
311: if (result == Evolver.EVOLVE_FAILURE) {
312: return false;
313: }
314: if (result == Evolver.EVOLVE_NEEDED) {
315: newVersion = true;
316: }
317: }
318:
319: /*
320: * We never need to use a custom reader because the physical key field
321: * formats never change. But we do create a new evolved format when
322: * a type changes (primitive <-> primitive wrapper) so that the new
323: * type information is correct.
324: */
325: if (newVersion) {
326: evolver.useEvolvedFormat(this , newFormat, newFormat);
327: } else {
328: evolver.useOldFormat(this , newFormat);
329: }
330: return true;
331: }
332: }
|