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