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