1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002,2008 Oracle. All rights reserved. 5 * 6 * $Id: PersistKeyCreator.java,v 1.1 2008/02/07 17:12:27 mark Exp $ 7 */ 8 9package com.sleepycat.persist.impl; 10 11import java.util.Collection; 12import java.util.Set; 13 14import com.sleepycat.bind.tuple.TupleBase; 15import com.sleepycat.db.DatabaseEntry; 16import com.sleepycat.db.DatabaseException; 17import com.sleepycat.db.ForeignMultiKeyNullifier; 18import com.sleepycat.db.SecondaryDatabase; 19import com.sleepycat.db.SecondaryKeyCreator; 20import com.sleepycat.db.SecondaryMultiKeyCreator; 21import com.sleepycat.persist.model.EntityMetadata; 22import com.sleepycat.persist.model.Relationship; 23import com.sleepycat.persist.model.SecondaryKeyMetadata; 24import com.sleepycat.persist.raw.RawObject; 25 26/** 27 * A persistence secondary key creator/nullifier. This class always uses 28 * rawAccess=true to avoid depending on the presence of the proxy class. 29 * 30 * @author Mark Hayes 31 */ 32public class PersistKeyCreator implements SecondaryKeyCreator, 33 SecondaryMultiKeyCreator, 34 ForeignMultiKeyNullifier { 35 36 static boolean isManyType(Class cls) { 37 return cls.isArray() || Collection.class.isAssignableFrom(cls); 38 } 39 40 private Catalog catalog; 41 private int priKeyFormatId; 42 private String keyName; 43 private Format keyFormat; 44 private boolean toMany; 45 46 /** 47 * Creates a key creator/nullifier for a given entity class and key name. 48 */ 49 public PersistKeyCreator(Catalog catalog, 50 EntityMetadata entityMeta, 51 String keyClassName, 52 SecondaryKeyMetadata secKeyMeta) { 53 this.catalog = catalog; 54 Format priKeyFormat = 55 catalog.getFormat(entityMeta.getPrimaryKey().getClassName()); 56 priKeyFormatId = priKeyFormat.getId(); 57 keyName = secKeyMeta.getKeyName(); 58 keyFormat = catalog.getFormat(keyClassName); 59 if (keyFormat == null) { 60 throw new IllegalArgumentException 61 ("Not a key class: " + keyClassName); 62 } 63 if (keyFormat.isPrimitive()) { 64 throw new IllegalArgumentException 65 ("Use a primitive wrapper class instead of class: " + 66 keyFormat.getClassName()); 67 } 68 Relationship rel = secKeyMeta.getRelationship(); 69 toMany = (rel == Relationship.ONE_TO_MANY || 70 rel == Relationship.MANY_TO_MANY); 71 } 72 73 public boolean createSecondaryKey(SecondaryDatabase secondary, 74 DatabaseEntry key, 75 DatabaseEntry data, 76 DatabaseEntry result) 77 throws DatabaseException { 78 79 if (toMany) { 80 throw new IllegalStateException(); 81 } 82 KeyLocation loc = moveToKey(key, data); 83 if (loc != null) { 84 RecordOutput output = new RecordOutput 85 (catalog, true /*rawAccess*/); 86 loc.format.copySecKey(loc.input, output); 87 TupleBase.outputToEntry(output, result); 88 return true; 89 } else { 90 /* Key field is not present or null. */ 91 return false; 92 } 93 } 94 95 public void createSecondaryKeys(SecondaryDatabase secondary, 96 DatabaseEntry key, 97 DatabaseEntry data, 98 Set results) 99 throws DatabaseException { 100 101 if (!toMany) { 102 throw new IllegalStateException(); 103 } 104 KeyLocation loc = moveToKey(key, data); 105 if (loc != null) { 106 loc.format.copySecMultiKey(loc.input, keyFormat, results); 107 } 108 /* Else key field is not present or null. */ 109 } 110 111 public boolean nullifyForeignKey(SecondaryDatabase secondary, 112 DatabaseEntry key, 113 DatabaseEntry data, 114 DatabaseEntry secKey) 115 throws DatabaseException { 116 117 /* Deserialize the entity and get its current class format. */ 118 RawObject entity = (RawObject) PersistEntityBinding.readEntity 119 (catalog, key, data, true /*rawAccess*/); 120 Format entityFormat = (Format) entity.getType(); 121 122 /* 123 * Set the key to null. For a TO_MANY key, pass the key object to be 124 * removed from the array/collection. 125 */ 126 Object secKeyObject = null; 127 if (toMany) { 128 secKeyObject = PersistKeyBinding.readKey 129 (keyFormat, catalog, secKey.getData(), secKey.getOffset(), 130 secKey.getSize(), true /*rawAccess*/); 131 } 132 if (entityFormat.nullifySecKey 133 (catalog, entity, keyName, secKeyObject)) { 134 135 /* 136 * Using the current format for the entity, serialize the modified 137 * entity back to the data entry. 138 */ 139 PersistEntityBinding.writeEntity 140 (entityFormat, catalog, entity, data, true /*rawAccess*/); 141 return true; 142 } else { 143 /* Key field is not present or null. */ 144 return false; 145 } 146 } 147 148 /** 149 * Returns the location from which the secondary key field can be copied. 150 */ 151 private KeyLocation moveToKey(DatabaseEntry priKey, DatabaseEntry data) { 152 153 RecordInput input = new RecordInput 154 (catalog, true /*rawAccess*/, priKey, priKeyFormatId, 155 data.getData(), data.getOffset(), data.getSize()); 156 int formatId = input.readPackedInt(); 157 Format entityFormat = catalog.getFormat(formatId); 158 Format fieldFormat = entityFormat.skipToSecKey(input, keyName); 159 if (fieldFormat != null) { 160 /* Returns null if key field is null. */ 161 return input.getKeyLocation(fieldFormat); 162 } else { 163 /* Key field is not present in this class. */ 164 return null; 165 } 166 } 167} 168