1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002-2009 Oracle. All rights reserved. 5 * 6 * $Id$ 7 */ 8 9package com.sleepycat.persist.impl; 10 11import com.sleepycat.bind.EntityBinding; 12import com.sleepycat.bind.tuple.TupleBase; 13import com.sleepycat.db.DatabaseEntry; 14import com.sleepycat.persist.raw.RawObject; 15 16/** 17 * A persistence entity binding for a given entity class. 18 * 19 * @author Mark Hayes 20 */ 21public class PersistEntityBinding implements EntityBinding { 22 23 PersistCatalog catalog; 24 Format entityFormat; 25 boolean rawAccess; 26 PersistKeyAssigner keyAssigner; 27 28 /** 29 * Creates a key binding for a given entity class. 30 */ 31 public PersistEntityBinding(PersistCatalog catalog, 32 String entityClassName, 33 boolean rawAccess) { 34 this.catalog = catalog; 35 entityFormat = getOrCreateFormat(catalog, entityClassName, rawAccess); 36 if (!entityFormat.isEntity()) { 37 throw new IllegalArgumentException 38 ("Not an entity class: " + entityClassName); 39 } 40 this.rawAccess = rawAccess; 41 } 42 43 public PersistKeyAssigner getKeyAssigner() { 44 return keyAssigner; 45 } 46 47 public Object entryToObject(DatabaseEntry key, DatabaseEntry data) { 48 return readEntity(catalog, key, data, rawAccess); 49 } 50 51 /** 52 * Creates the instance, reads the entity key first to track visited 53 * entities correctly, then reads the data and returns the entity. 54 * 55 * This is a special case of EntityInput.readObject for a top level entity. 56 * Special treatments are: 57 * - The formatId must be >= 0; since this is the top level instance, it 58 * cannot refer to a visited object nor be a null reference. 59 * - The resulting entity is not added to the visited object set; entities 60 * cannot be referenced by another (or the same) entity. 61 * - Reader.readPriKey must be called prior to calling Reader.readObject. 62 */ 63 static Object readEntity(Catalog catalog, 64 DatabaseEntry key, 65 DatabaseEntry data, 66 boolean rawAccess) { 67 RecordInput keyInput = new RecordInput 68 (catalog, rawAccess, null, 0, 69 key.getData(), key.getOffset(), key.getSize()); 70 RecordInput dataInput = new RecordInput 71 (catalog, rawAccess, null, 0, 72 data.getData(), data.getOffset(), data.getSize()); 73 int formatId = dataInput.readPackedInt(); 74 Format format = catalog.getFormat(formatId); 75 Reader reader = format.getReader(); 76 Object entity = reader.newInstance(dataInput, rawAccess); 77 reader.readPriKey(entity, keyInput, rawAccess); 78 return reader.readObject(entity, dataInput, rawAccess); 79 } 80 81 public void objectToData(Object entity, DatabaseEntry data) { 82 Format format = getValidFormat(entity); 83 writeEntity(format, catalog, entity, data, rawAccess); 84 } 85 86 /** 87 * Writes the formatId and object, and returns the bytes. 88 * 89 * This is a special case of EntityOutput.writeObject for a top level 90 * entity. Special treatments are: 91 * - The entity may not be null. 92 * - The entity is not added to the visited object set nor checked for 93 * existence in the visited object set; entities cannot be referenced by 94 * another (or the same) entity. 95 */ 96 static void writeEntity(Format format, 97 Catalog catalog, 98 Object entity, 99 DatabaseEntry data, 100 boolean rawAccess) { 101 RecordOutput output = new RecordOutput(catalog, rawAccess); 102 output.writePackedInt(format.getId()); 103 format.writeObject(entity, output, rawAccess); 104 TupleBase.outputToEntry(output, data); 105 } 106 107 public void objectToKey(Object entity, DatabaseEntry key) { 108 109 /* 110 * Write the primary key field as a special case since the output 111 * format is for a key binding, not entity data. 112 */ 113 Format format = getValidFormat(entity); 114 RecordOutput output = new RecordOutput(catalog, rawAccess); 115 116 /* Write the primary key and return the bytes. */ 117 format.writePriKey(entity, output, rawAccess); 118 TupleBase.outputToEntry(output, key); 119 } 120 121 /** 122 * Returns the format for the given entity and validates it, throwing an 123 * exception if it is invalid for this binding. 124 */ 125 private Format getValidFormat(Object entity) { 126 127 /* A null entity is not allowed. */ 128 if (entity == null) { 129 throw new IllegalArgumentException("An entity may not be null"); 130 } 131 132 /* 133 * Get the format. getFormat throws IllegalArgumentException if the 134 * class is not persistent. 135 */ 136 Format format; 137 if (rawAccess) { 138 if (!(entity instanceof RawObject)) { 139 throw new IllegalArgumentException 140 ("Entity must be a RawObject"); 141 } 142 format = (Format) ((RawObject) entity).getType(); 143 } else { 144 format = catalog.getFormat 145 (entity.getClass(), true /*checkEntitySubclassIndexes*/); 146 } 147 148 /* Check that the entity class/subclass is valid for this binding. */ 149 if (format.getEntityFormat() != entityFormat) { 150 throw new IllegalArgumentException 151 ("The entity class (" + format.getClassName() + 152 ") must be this entity class or a subclass of it: " + 153 entityFormat.getClassName()); 154 } 155 156 return format; 157 } 158 159 /** 160 * Utility method for getting or creating a format as appropriate for 161 * bindings and key creators. 162 */ 163 static Format getOrCreateFormat(Catalog catalog, 164 String clsName, 165 boolean rawAccess) { 166 if (rawAccess) { 167 Format format = catalog.getFormat(clsName); 168 if (format == null) { 169 throw new IllegalArgumentException 170 ("Not a persistent class: " + clsName); 171 } 172 return format; 173 } else { 174 Class cls = SimpleCatalog.keyClassForName(clsName); 175 return catalog.getFormat(cls, true /*checkEntitySubclassIndexes*/); 176 } 177 } 178} 179