1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002,2008 Oracle. All rights reserved. 5 * 6 * $Id: RecordOutput.java,v 1.1 2008/02/07 17:12:27 mark Exp $ 7 */ 8 9package com.sleepycat.persist.impl; 10 11import com.sleepycat.bind.tuple.TupleOutput; 12import com.sleepycat.persist.raw.RawObject; 13 14/** 15 * Implements EntityOutput to write record key-data pairs. Extends TupleOutput 16 * to implement the subset of TupleOutput methods that are defined in the 17 * EntityOutput interface. 18 * 19 * @author Mark Hayes 20 */ 21class RecordOutput extends TupleOutput implements EntityOutput { 22 23 private Catalog catalog; 24 private boolean rawAccess; 25 private VisitedObjects visited; 26 27 /** 28 * Creates a new output with an empty/null VisitedObjects set. 29 */ 30 RecordOutput(Catalog catalog, boolean rawAccess) { 31 32 super(); 33 this.catalog = catalog; 34 this.rawAccess = rawAccess; 35 } 36 37 /** 38 * @see EntityInput#writeObject 39 */ 40 public void writeObject(Object o, Format fieldFormat) { 41 42 /* For a null instance, write a zero format ID. */ 43 if (o == null) { 44 writePackedInt(Format.ID_NULL); 45 return; 46 } 47 48 /* 49 * For an already visited instance, output a reference to it. The 50 * reference is the negation of the visited offset minus one. 51 */ 52 if (visited != null) { 53 int offset = visited.getOffset(o); 54 if (offset == VisitedObjects.PROHIBIT_REF_OFFSET) { 55 throw new IllegalArgumentException 56 (VisitedObjects.PROHIBIT_NESTED_REF_MSG); 57 } 58 if (offset > 0) { 59 writePackedInt(-(offset + 1)); 60 return; 61 } 62 } 63 64 /* 65 * Get and validate the format. Catalog.getFormat(Class) throws 66 * IllegalArgumentException if the class is not persistent. We don't 67 * need to check the fieldFormat (and it will be null) for non-raw 68 * access because field type checking is enforced by Java. 69 */ 70 Format format; 71 if (rawAccess) { 72 format = RawAbstractInput.checkRawType(catalog, o, fieldFormat); 73 } else { 74 format = catalog.getFormat(o.getClass()); 75 } 76 if (format.getProxiedFormat() != null) { 77 throw new IllegalArgumentException 78 ("May not store proxy classes directly: " + 79 format.getClassName()); 80 } 81 if (format.isEntity()) { 82 throw new IllegalArgumentException 83 ("References to entities are not allowed: " + 84 o.getClass().getName()); 85 } 86 87 /* 88 * Remember that we visited this instance. Certain formats 89 * (ProxiedFormat for example) prohibit nested fields that reference 90 * the parent object. [#15815] 91 */ 92 if (visited == null) { 93 visited = new VisitedObjects(); 94 } 95 boolean prohibitNestedRefs = format.areNestedRefsProhibited(); 96 int visitedOffset = size(); 97 int visitedIndex = visited.add(o, prohibitNestedRefs ? 98 VisitedObjects.PROHIBIT_REF_OFFSET : 99 visitedOffset); 100 101 /* Finally, write the formatId and object value. */ 102 writePackedInt(format.getId()); 103 format.writeObject(o, this, rawAccess); 104 105 /* Always allow references from siblings that follow. */ 106 if (prohibitNestedRefs) { 107 visited.setOffset(visitedIndex, visitedOffset); 108 } 109 } 110 111 /** 112 * @see EntityInput#writeKeyObject 113 */ 114 public void writeKeyObject(Object o, Format fieldFormat) { 115 116 /* Key objects must not be null and must be of the declared class. */ 117 if (o == null) { 118 throw new IllegalArgumentException 119 ("Key field object may not be null"); 120 } 121 Format format; 122 if (rawAccess) { 123 if (o instanceof RawObject) { 124 format = (Format) ((RawObject) o).getType(); 125 } else { 126 format = catalog.getFormat(o.getClass()); 127 /* Expect primitive wrapper class in raw mode. */ 128 if (fieldFormat.isPrimitive()) { 129 fieldFormat = fieldFormat.getWrapperFormat(); 130 } 131 } 132 } else { 133 format = catalog.getFormat(o.getClass()); 134 } 135 if (fieldFormat != format) { 136 throw new IllegalArgumentException 137 ("The key field object class (" + o.getClass().getName() + 138 ") must be the field's declared class: " + 139 fieldFormat.getClassName()); 140 } 141 142 /* Write the object value (no formatId is written for keys). */ 143 fieldFormat.writeObject(o, this, rawAccess); 144 } 145 146 /** 147 * @see EntityInput#registerPriKeyObject 148 */ 149 public void registerPriKeyObject(Object o) { 150 151 /* 152 * PRI_KEY_VISITED_OFFSET is used as the visited offset to indicate 153 * that the visited object is stored in the primary key byte array. 154 */ 155 if (visited == null) { 156 visited = new VisitedObjects(); 157 } 158 visited.add(o, VisitedObjects.PRI_KEY_VISITED_OFFSET); 159 } 160 161 /** 162 * @see EntityInput#writeArrayLength 163 */ 164 public void writeArrayLength(int length) { 165 writePackedInt(length); 166 } 167 168 /** 169 * @see EntityInput#writeEnumConstant 170 */ 171 public void writeEnumConstant(String[] names, int index) { 172 writePackedInt(index); 173 } 174} 175