001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: RecordOutput.java,v 1.4.2.4 2008/01/07 15:14:20 cwl Exp $
007: */
008:
009: package com.sleepycat.persist.impl;
010:
011: import com.sleepycat.bind.tuple.TupleOutput;
012: import com.sleepycat.persist.raw.RawObject;
013:
014: /**
015: * Implements EntityOutput to write record key-data pairs. Extends TupleOutput
016: * to implement the subset of TupleOutput methods that are defined in the
017: * EntityOutput interface.
018: *
019: * @author Mark Hayes
020: */
021: class RecordOutput extends TupleOutput implements EntityOutput {
022:
023: private Catalog catalog;
024: private boolean rawAccess;
025: private VisitedObjects visited;
026:
027: /**
028: * Creates a new output with an empty/null VisitedObjects set.
029: */
030: RecordOutput(Catalog catalog, boolean rawAccess) {
031:
032: super ();
033: this .catalog = catalog;
034: this .rawAccess = rawAccess;
035: }
036:
037: /**
038: * @see EntityInput#writeObject
039: */
040: public void writeObject(Object o, Format fieldFormat) {
041:
042: /* For a null instance, write a zero format ID. */
043: if (o == null) {
044: writePackedInt(Format.ID_NULL);
045: return;
046: }
047:
048: /*
049: * For an already visited instance, output a reference to it. The
050: * reference is the negation of the visited offset minus one.
051: */
052: if (visited != null) {
053: int offset = visited.getOffset(o);
054: if (offset == VisitedObjects.PROHIBIT_REF_OFFSET) {
055: throw new IllegalArgumentException(
056: VisitedObjects.PROHIBIT_NESTED_REF_MSG);
057: }
058: if (offset > 0) {
059: writePackedInt(-(offset + 1));
060: return;
061: }
062: }
063:
064: /*
065: * Get and validate the format. Catalog.getFormat(Class) throws
066: * IllegalArgumentException if the class is not persistent. We don't
067: * need to check the fieldFormat (and it will be null) for non-raw
068: * access because field type checking is enforced by Java.
069: */
070: Format format;
071: if (rawAccess) {
072: format = RawAbstractInput.checkRawType(catalog, o,
073: fieldFormat);
074: } else {
075: format = catalog.getFormat(o.getClass());
076: }
077: if (format.getProxiedFormat() != null) {
078: throw new IllegalArgumentException(
079: "May not store proxy classes directly: "
080: + format.getClassName());
081: }
082: if (format.isEntity()) {
083: throw new IllegalArgumentException(
084: "References to entities are not allowed: "
085: + o.getClass().getName());
086: }
087:
088: /*
089: * Remember that we visited this instance. Certain formats
090: * (ProxiedFormat for example) prohibit nested fields that reference
091: * the parent object. [#15815]
092: */
093: if (visited == null) {
094: visited = new VisitedObjects();
095: }
096: boolean prohibitNestedRefs = format.areNestedRefsProhibited();
097: int visitedOffset = size();
098: int visitedIndex = visited.add(o,
099: prohibitNestedRefs ? VisitedObjects.PROHIBIT_REF_OFFSET
100: : visitedOffset);
101:
102: /* Finally, write the formatId and object value. */
103: writePackedInt(format.getId());
104: format.writeObject(o, this , rawAccess);
105:
106: /* Always allow references from siblings that follow. */
107: if (prohibitNestedRefs) {
108: visited.setOffset(visitedIndex, visitedOffset);
109: }
110: }
111:
112: /**
113: * @see EntityInput#writeKeyObject
114: */
115: public void writeKeyObject(Object o, Format fieldFormat) {
116:
117: /* Key objects must not be null and must be of the declared class. */
118: if (o == null) {
119: throw new IllegalArgumentException(
120: "Key field object may not be null");
121: }
122: Format format;
123: if (rawAccess) {
124: if (o instanceof RawObject) {
125: format = (Format) ((RawObject) o).getType();
126: } else {
127: format = catalog.getFormat(o.getClass());
128: /* Expect primitive wrapper class in raw mode. */
129: if (fieldFormat.isPrimitive()) {
130: fieldFormat = fieldFormat.getWrapperFormat();
131: }
132: }
133: } else {
134: format = catalog.getFormat(o.getClass());
135: }
136: if (fieldFormat != format) {
137: throw new IllegalArgumentException(
138: "The key field object class ("
139: + o.getClass().getName()
140: + ") must be the field's declared class: "
141: + fieldFormat.getClassName());
142: }
143:
144: /* Write the object value (no formatId is written for keys). */
145: fieldFormat.writeObject(o, this , rawAccess);
146: }
147:
148: /**
149: * @see EntityInput#registerPriKeyObject
150: */
151: public void registerPriKeyObject(Object o) {
152:
153: /*
154: * PRI_KEY_VISITED_OFFSET is used as the visited offset to indicate
155: * that the visited object is stored in the primary key byte array.
156: */
157: if (visited == null) {
158: visited = new VisitedObjects();
159: }
160: visited.add(o, VisitedObjects.PRI_KEY_VISITED_OFFSET);
161: }
162:
163: /**
164: * @see EntityInput#writeArrayLength
165: */
166: public void writeArrayLength(int length) {
167: writePackedInt(length);
168: }
169:
170: /**
171: * @see EntityInput#writeEnumConstant
172: */
173: public void writeEnumConstant(String[] names, int index) {
174: writePackedInt(index);
175: }
176: }
|