Package com.sleepycat.persist.impl

Source Code of com.sleepycat.persist.impl.Format

/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2002, 2011 Oracle and/or its affiliates.  All rights reserved.
*
*/

package com.sleepycat.persist.impl;

import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.sleepycat.compat.DbCompat;
import com.sleepycat.persist.evolve.Converter;
import com.sleepycat.persist.model.ClassMetadata;
import com.sleepycat.persist.model.EntityMetadata;
import com.sleepycat.persist.model.EntityModel;
import com.sleepycat.persist.model.FieldMetadata;
import com.sleepycat.persist.model.PrimaryKeyMetadata;
import com.sleepycat.persist.model.SecondaryKeyMetadata;
import com.sleepycat.persist.raw.RawField;
import com.sleepycat.persist.raw.RawObject;
import com.sleepycat.persist.raw.RawType;
import com.sleepycat.je.utilint.IdentityHashMap;

/**
* The base class for all object formats.  Formats are used to define the
* stored layout for all persistent classes, including simple types.
*
* The design documentation below describes the storage format for entities and
* its relationship to information stored per format in the catalog.
*
* Requirements
* ------------
* + Provides EntityBinding for objects and EntryBinding for keys.
* + Provides SecondaryKeyCreator, SecondaryMultiKeyCreator and
*   SecondaryMultiKeyNullifier (SecondaryKeyNullifier is redundant).
* + Works with reflection and bytecode enhancement.
* + For reflection only, works with any entity model not just annotations.
* + Bindings are usable independently of the persist API.
* + Performance is almost equivalent to hand coded tuple bindings.
* + Small performance penalty for compatible class changes (new fields,
*   widening).
* + Secondary key create/nullify do not have to deserialize the entire record;
*   in other words, store secondary keys at the start of the data.
*
* Class Format
* ------------
* Every distinct class format is given a unique format ID.  Class IDs are not
* equivalent to class version numbers (as in the version property of @Entity
* and @Persistent) because the format can change when the version number does
* not.  Changes that cause a unique format ID to be assigned are:
*
* + Add field.
* + Widen field type.
* + Change primitive type to primitive wrapper class.
* + Add or drop secondary key.
* + Any incompatible class change.
*
* The last item, incompatible class changes, also correspond to a class
* version change.
*
* For each distinct class format the following information is conceptually
* stored in the catalog, keyed by format ID.
*
* - Class name
* - Class version number
* - Superclass format
* - Kind: simple, enum, complex, array
* - For kind == simple:
*     - Primitive class
* - For kind == enum:
*     - Array of constant names, sorted by name.
* - For kind == complex:
*     - Primary key fieldInfo, or null if no primary key is declared
*     - Array of secondary key fieldInfo, sorted by field name
*     - Array of other fieldInfo, sorted by field name
* - For kind == array:
*     - Component class format
*     - Number of array dimensions
* - Other metadata for RawType
*
* Where fieldInfo is:
*     - Field name
*     - Field class
*     - Other metadata for RawField
*
* Data Layout
* -----------
* For each entity instance the data layout is as follows:
*
*   instanceData: formatId keyFields... nonKeyFields...
*   keyFields:    fieldValue...
*   nonKeyFields: fieldValue...
*
* The formatId is the (positive non-zero) ID of a class format, defined above.
* This is ID of the most derived class of the instance.  It is stored as a
* packed integer.
*
* Following the format ID, zero or more sets of secondary key field values
* appear, followed by zero or more sets of other class field values.
*
* The keyFields are the sets of secondary key fields for each class in order
* of the highest superclass first.  Within a class, fields are ordered by
* field name.
*
* The nonKeyFields are the sets of other non-key fields for each class in
* order of the highest superclass first.  Within a class, fields are ordered
* by field name.
*
* A field value is:
*
*   fieldValue:   primitiveValue
*               | nullId
*               | instanceRef
*               | instanceData
*               | simpleValue
*               | enumValue
*               | arrayValue
*
* For a primitive type, a primitive value is used as defined for tuple
* bindings.  For float and double, sorted float and sorted double tuple values
* are used.
*
* For a non-primitive type with a null value, a nullId is used that has a zero
* (illegal formatId) value.  This includes String and other simple reference
* types.  The formatId is stored as a packed integer, meaning that it is
* stored as a single zero byte.
*
* For a non-primitive type, an instanceRef is used for a non-null instance
* that appears earlier in the data byte array.  An instanceRef is the negation
* of the byte offset of the instanceData that appears earlier.  It is stored
* as a packed integer.
*
* The remaining rules apply only to reference types with non-null values that
* do not appear earlier in the data array.
*
* For an array type, an array formatId is used that identifies the component
* type and the number of array dimensions.  This is followed by an array
* length (stored as a packed integer) and zero or more fieldValue elements.
* For an array with N+1 dimensions where N is greater than zero, the leftmost
* dimension is enumerated such that each fieldValue element is itself an array
* of N dimensions or null.
*
*   arrayValue:  formatId length fieldValue...
*
* For an enum type, an enumValue is used, consisting of a formatId that
* identifies the enum class and an enumIndex (stored as a packed integer) that
* identifies the constant name in the enum constant array of the enum class
* format:
*
*   enumValue:   formatId enumIndex
*
* For a simple type, a simpleValue is used.  This consists of the formatId
* that identifies the class followed by the simple type value.  For a
* primitive wrapper type the simple type value is the corresponding primitive,
* for a Date it is the milliseconds as a long primitive, and for BigInteger or
* BigDecimal it is a byte array as defined for tuple bindings of these types.
*
*   simpleValue: formatId value
*
* For all other complex types, an instanceData is used, which is defined
* above.
*
* Secondary Keys
* --------------
* For secondary key support we must account for writing and nullifying
* specific keys.  Rather than instantiating the entity and then performing
* the secondary key operation, we strive to perform the secondary key
* operation directly on the byte format.
*
* To create a secondary key we skip over other fields and then copy the bytes
* of the embedded key.  This approach is very efficient because a) the entity
* is not instantiated, and b) the secondary keys are stored at the beginning
* of the byte format and can be quickly read.
*
* To nullify we currently instantiate the raw entity, set the key field to null
* (or remove it from the array/collection), and convert the raw entity back to
* bytes.  Although the performance of this approach is not ideal because it
* requires serialization, it avoids the complexity of modifying the packed
* serialized format directly, adjusting references to key objects, etc.  Plus,
* when we nullify a key we are going to write the record, so the serialization
* overhead may not be significant.  For the record, I tried implementing
* nullification of the bytes directly and found it was much too complex.
*
* Lifecycle
* ---------
* Format are managed by a Catalog class.  Simple formats are managed by
* SimpleCatalog, and are copied from the SimpleCatalog by PersistCatalog.
* Other formats are managed by PersistCatalog.  The lifecycle of a format
* instance is:
*
* - Constructed by the catalog when a format is requested for a Class
*   that currently has no associated format.
*
* - The catalog calls setId() and adds the format to its format list
*   (indexed by format id) and map (keyed by class name).
*
* - The catalog calls collectRelatedFormats(), where a format can create
*   additional formats that it needs, or that should also be persistent.
*
* - The catalog calls initializeIfNeeded(), which calls the initialize()
*   method of the format class.
*
* - initialize() should initialize any transient fields in the format.
*   initialize() can assume that all related formats are available in the
*   catalog.  It may call initializeIfNeeded() for those related formats, if
*   it needs to interact with an initialized related format; this does not
*   cause a cycle, because initializeIfNeeded() does nothing for an already
*   initialized format.
*
* - The catalog creates a group of related formats at one time, and then
*   writes its entire list of formats to the catalog DB as a single record.
*   This grouping reduces the number of writes.
*
* - When a catalog is opened and the list of existing formats is read.  After
*   a format is deserialized, its initializeIfNeeded() method is called.
*   setId() and collectRelatedFormats() are not called, since the ID and
*   related formats are stored in serialized fields.
*
* - There are two modes for opening an existing catalog: raw mode and normal
*   mode.  In raw mode, the old format is used regardless of whether it
*   matches the current class definition; in fact the class is not accessed
*   and does not need to be present.
*
* - In normal mode, for each existing format that is initialized, a new format
*   is also created based on the current class and metadata definition.  If
*   the two formats are equal, the new format is discarded.  If they are
*   unequal, the new format becomes the current format and the old format's
*   evolve() method is called.  evolve() is responsible for adjusting the
*   old format for class evolution.  Any number of non-current formats may
*   exist for a given class, and are setup to evolve the single current format
*   for the class.
*
* @author Mark Hayes
*/
public abstract class Format implements Reader, RawType, Serializable {

    private static final long serialVersionUID = 545633644568489850L;

    /** Null reference. */
    static final int ID_NULL     = 0;
    /** Object */
    static final int ID_OBJECT   = 1;
    /** Boolean */
    static final int ID_BOOL     = 2;
    static final int ID_BOOL_W   = 3;
    /** Byte */
    static final int ID_BYTE     = 4;
    static final int ID_BYTE_W   = 5;
    /** Short */
    static final int ID_SHORT    = 6;
    static final int ID_SHORT_W  = 7;
    /** Integer */
    static final int ID_INT      = 8;
    static final int ID_INT_W    = 9;
    /** Long */
    static final int ID_LONG     = 10;
    static final int ID_LONG_W   = 11;
    /** Float */
    static final int ID_FLOAT    = 12;
    static final int ID_FLOAT_W  = 13;
    /** Double */
    static final int ID_DOUBLE   = 14;
    static final int ID_DOUBLE_W = 15;
    /** Character */
    static final int ID_CHAR     = 16;
    static final int ID_CHAR_W   = 17;
    /** String */
    static final int ID_STRING   = 18;
    /** BigInteger */
    static final int ID_BIGINT   = 19;
    /** BigDecimal */
    static final int ID_BIGDEC   = 20;
    /** Date */
    static final int ID_DATE     = 21;
    /** Number */
    static final int ID_NUMBER   = 22;

    /** First simple type. */
    static final int ID_SIMPLE_MIN  = 2;
    /** Last simple type. */
    static final int ID_SIMPLE_MAX  = 21;
    /** Last predefined ID, after which dynamic IDs are assigned. */
    static final int ID_PREDEFINED  = 30;

    static boolean isPredefined(Format format) {
        return format.getId() <= ID_PREDEFINED;
    }

    private int id;
    private String className;
    private Reader reader;
    private Format superFormat;
    private Format latestFormat;
    private Format previousFormat;
    private Set<String> supertypes;
    private boolean deleted;
    private boolean unused;
    private transient Catalog catalog;
    private transient Class type;
    private transient Format proxiedFormat;
    private transient boolean initialized;

    /**
     * Creates a new format for a given class.
     */
    Format(final Catalog catalog, final Class type) {
        this(catalog, type.getName());
        this.type = type;
        addSupertypes();
    }

    /**
     * Creates a format for class evolution when no class may be present.
     */
    Format(final Catalog catalog, final String className) {
        assert catalog != null;
        assert className != null;
        this.catalog = catalog;
        this.className = className;
        latestFormat = this;
        supertypes = new HashSet<String>();
    }

    /**
     * Special handling for JE 3.0.12 beta formats.
     */
    void migrateFromBeta(Map<String, Format> formatMap) {
        if (latestFormat == null) {
            latestFormat = this;
        }
    }

    /**
     * Initialize transient catalog field after deserialization.  This must
     * occur before any other usage.
     */
    void initCatalog(final Catalog catalog) {
        assert catalog != null;
        this.catalog = catalog;
    }

    final boolean isNew() {
        return id == 0;
    }

    final Catalog getCatalog() {
        return catalog;
    }

    /**
     * Returns the format ID.
     */
    public final int getId() {
        return id;
    }

    /**
     * Called by the Catalog to set the format ID when a new format is added to
     * the format list, before calling initializeIfNeeded().
     */
    final void setId(int id) {
        this.id = id;
    }

    /**
     * Returns the class that this format represents.  This method will return
     * null in rawAccess mode, or for an unevolved format.
     */
    final Class getType() {
        return type;
    }

    /**
     * Called to get the type when it is known to exist for an uninitialized
     * format.
     */
    final Class getExistingType() {
        assert catalog != null;
        if (type == null) {
            try {
                type = catalog.resolveClass(className);
            } catch (ClassNotFoundException e) {
                throw DbCompat.unexpectedException(e);
            }
        }
        return type;
    }

    /**
     * Returns the object for reading objects of the latest format.  For the
     * latest version format, 'this' is returned.  For prior version formats, a
     * reader that converts this version to the latest version is returned.
     */
    final Reader getReader() {

        /*
         * For unit testing, record whether any un-evolved formats are
         * encountered.
         */
        if (this != reader) {
            PersistCatalog.unevolvedFormatsEncountered = true;
        }

        return reader;
    }

    /**
     * Changes the reader during format evolution.
     */
    final void setReader(Reader reader) {
        this.reader = reader;
    }

    /**
     * Returns the format of the superclass.
     */
    final Format getSuperFormat() {
        return superFormat;
    }

    /**
     * Called to set the format of the superclass during initialize().
     */
    final void setSuperFormat(Format superFormat) {
        this.superFormat = superFormat;
    }

    /**
     * Returns the format that is proxied by this format.  If non-null is
     * returned, then this format is a PersistentProxy.
     */
    final Format getProxiedFormat() {
        return proxiedFormat;
    }

    /**
     * Called by ProxiedFormat to set the proxied format.
     */
    final void setProxiedFormat(Format proxiedFormat) {
        this.proxiedFormat = proxiedFormat;
    }

    /**
     * If this is the latest/evolved format, returns this; otherwise, returns
     * the current version of this format.  Note that this WILL return a
     * format for a deleted class if the latest format happens to be deleted.
     */
    final Format getLatestVersion() {
        return latestFormat;
    }

    /**
     * Returns the previous version of this format in the linked list of
     * versions, or null if this is the only version.
     */
    public final Format getPreviousVersion() {
        return previousFormat;
    }

    /**
     * Called by Evolver to set the latest format when this old format is
     * evolved.
     */
    final void setLatestVersion(Format newFormat) {

        /*
         * If this old format is the former latest version, link it to the new
         * latest version.  This creates a singly linked list of versions
         * starting with the latest.
         */
        if (latestFormat == this) {
            newFormat.previousFormat = this;
        }

        latestFormat = newFormat;
    }

    /**
     * Returns whether the class for this format was deleted.
     */
    public final boolean isDeleted() {
        return deleted;
    }

    /**
     * Called by the Evolver when applying a Deleter mutation.
     */
    final void setDeleted(boolean deleted) {
        this.deleted = deleted;
    }

    /**
     * Called by the Evolver for a format that is never referenced.
     */
    final void setUnused(boolean unused) {
        this.unused = unused;
    }

    /**
     * Called by the Evolver with true when an entity format or any of its
     * nested format were changed.  Called by Store.evolve when an entity has
     * been fully converted.  Overridden by ComplexFormat.
     */
    void setEvolveNeeded(boolean needed) {
        throw DbCompat.unexpectedState();
    }

    /**
     * Overridden by ComplexFormat.
     */
    boolean getEvolveNeeded() {
        throw DbCompat.unexpectedState();
    }

    /**
     * For an entity format, returns whether the entity was written using the
     * new String format.  For a non-entity format, this method should not be
     * called.
     *
     * Overridden by ComplexFormat.
     */
    boolean getNewStringFormat() {
        throw DbCompat.unexpectedState();
    }

    final boolean isInitialized() {
        return initialized;
    }

    /**
     * Called by the Catalog to initialize a format, and may also be called
     * during initialize() for a related format to ensure that the related
     * format is initialized.  This latter case is allowed to support
     * bidirectional dependencies.  This method will do nothing if the format
     * is already intialized.
     */
    final void initializeIfNeeded(Catalog catalog, EntityModel model) {
        assert catalog != null;

        if (!initialized) {
            initialized = true;
            this.catalog = catalog;

            /* Initialize objects serialized by an older Format class. */
            if (latestFormat == null) {
                latestFormat = this;
            }
            if (reader == null) {
                reader = this;
            }

            /*
             * The class is only guaranteed to be available in live (not raw)
             * mode, for the current version of the format.
             */
            if (type == null &&
                isCurrentVersion() &&
                (isSimple() || !catalog.isRawAccess())) {
                getExistingType();
            }

            /* Perform subclass-specific initialization. */
            initialize(catalog, model,
                       catalog.getInitVersion(this, false /*forReader*/));
            reader.initializeReader
                (catalog, model,
                 catalog.getInitVersion(this, true /*forReader*/),
                 this);
        }
    }

    /**
     * Called to initialize a separate Reader implementation.  This method is
     * called when no separate Reader exists, and does nothing.
     */
    public void initializeReader(Catalog catalog,
                                 EntityModel model,
                                 int initVersion,
                                 Format oldFormat) {
    }

    /**
     * Adds all interfaces and superclasses to the supertypes set.
     */
    private void addSupertypes() {
        addInterfaces(type);
        Class stype = type.getSuperclass();
        while (stype != null && stype != Object.class) {
            supertypes.add(stype.getName());
            addInterfaces(stype);
            stype = stype.getSuperclass();
        }
    }

    /**
     * Recursively adds interfaces to the supertypes set.
     */
    private void addInterfaces(Class cls) {
        Class[] interfaces = cls.getInterfaces();
        for (Class iface : interfaces) {
            if (iface != Enhanced.class) {
                supertypes.add(iface.getName());
                addInterfaces(iface);
            }
        }
    }

    /**
     * Certain formats (ProxiedFormat for example) prohibit nested fields that
     * reference the parent object. [#15815]
     */
    boolean areNestedRefsProhibited() {
        return false;
    }

    /* -- Start of RawType interface methods. -- */

    public String getClassName() {
        return className;
    }

    public int getVersion() {
        ClassMetadata meta = getClassMetadata();
        if (meta != null) {
            return meta.getVersion();
        } else {
            return 0;
        }
    }

    public Format getSuperType() {
        return superFormat;
    }

    /* -- RawType methods that are overridden as needed in subclasses. -- */

    public boolean isSimple() {
        return false;
    }

    public boolean isPrimitive() {
        return false;
    }

    public boolean isEnum() {
        return false;
    }

    public List<String> getEnumConstants() {
        return null;
    }

    public boolean isArray() {
        return false;
    }

    public int getDimensions() {
        return 0;
    }

    public Format getComponentType() {
        return null;
    }

    public Map<String, RawField> getFields() {
        return null;
    }

    public ClassMetadata getClassMetadata() {
        return null;
    }

    public EntityMetadata getEntityMetadata() {
        return null;
    }

    /* -- End of RawType methods. -- */

    /* -- Methods that may optionally be overridden by subclasses. -- */

    /**
     * Called by EntityOutput in rawAccess mode to determine whether an object
     * type is allowed to be assigned to a given field type.
     */
    boolean isAssignableTo(Format format) {
        if (proxiedFormat != null) {
            return proxiedFormat.isAssignableTo(format);
        } else {
            return format == this ||
                   format.id == ID_OBJECT ||
                   supertypes.contains(format.className);
        }
    }

    /**
     * For primitive types only, returns their associated wrapper type.
     */
    Format getWrapperFormat() {
        return null;
    }

    /**
     * Returns whether this format class is an entity class.
     */
    boolean isEntity() {
        return false;
    }

    /**
     * Returns whether this class is present in the EntityModel.  Returns false
     * for a simple type, array type, or enum type.
     */
    boolean isModelClass() {
        return false;
    }

    /**
     * For an entity class or subclass, returns the base entity class; returns
     * null in other cases.
     */
    ComplexFormat getEntityFormat() {
        return null;
    }

    /**
     * Called for an existing format that may not equal the current format for
     * the same class.
     *
     * <p>If this method returns true, then it must have determined one of two
     * things:
     *  - that the old and new formats are equal, and it must have called
     *  Evolver.useOldFormat; or
     *  - that the old format can be evolved to the new format, and it must
     *  have called Evolver.useEvolvedFormat.</p>
     *
     * <p>If this method returns false, then it must have determined that the
     * old format could not be evolved to the new format, and it must have
     * called Evolver.addInvalidMutation, addMissingMutation or
     * addEvolveError.</p>
     */
    abstract boolean evolve(Format newFormat, Evolver evolver);

    /**
     * Called when a Converter handles evolution of a class, but we may still
     * need to evolve the metadata.
     */
    boolean evolveMetadata(Format newFormat,
                           Converter converter,
                           Evolver evolver) {
        return true;
    }

    /**
     * Returns whether this format is the current format for its class.  If
     * false is returned, this format is setup to evolve to the current format.
     */
    final boolean isCurrentVersion() {
        return latestFormat == this && !deleted;
    }

    /**
     * Returns whether this format has the same class as the given format,
     * irrespective of version changes and renaming.
     */
    final boolean isSameClass(Format other) {
        return latestFormat == other.latestFormat;
    }

    /* -- Abstract methods that must be implemented by subclasses. -- */

    /**
     * Initializes an uninitialized format, initializing its related formats
     * (superclass formats and array component formats) first.
     */
    abstract void initialize(Catalog catalog,
                             EntityModel model,
                             int initVersion);

    /**
     * Calls catalog.createFormat for formats that this format depends on, or
     * that should also be persistent.
     */
    abstract void collectRelatedFormats(Catalog catalog,
                                        Map<String, Format> newFormats);

    /*
     * The remaining methods are used to read objects from data bytes via
     * EntityInput, and to write objects as data bytes via EntityOutput.
     * Ultimately these methods call methods in the Accessor interface to
     * get/set fields in the object.  Most methods have a rawAccess parameter
     * that determines whether the object is a raw object or a real persistent
     * object.
     *
     * The first group of methods are abstract and must be implemented by
     * format classes.  The second group have default implementations that
     * throw UnsupportedOperationException and may optionally be overridden.
     */

    /**
     * Creates an array of the format's class of the given length, as if
     * Array.newInstance(getType(), len) were called.  Formats implement this
     * method for specific classes, or call the accessor, to avoid the
     * reflection overhead of Array.newInstance.
     */
    abstract Object newArray(int len);

    /**
     * Creates a new instance of the target class using its default
     * constructor.  Normally this creates an empty object, and readObject() is
     * called next to fill in the contents.  This is done in two steps to allow
     * the instance to be registered by EntityInput before reading the
     * contents.  This allows the fields in an object or a nested object to
     * refer to the parent object in a graph.
     *
     * Alternatively, this method may read all or the first portion of the
     * data, rather than that being done by readObject().  This is required for
     * simple types and enums, where the object cannot be created without
     * reading the data.  In these cases, there is no possibility that the
     * parent object will be referenced by the child object in the graph.  It
     * should not be done in other cases, or the graph references may not be
     * maintained faithfully.
     *
     * Is public only in order to implement the Reader interface.  Note that
     * this method should only be called directly in raw conversion mode or
     * during conversion of an old format.  Normally it should be called via
     * the getReader method and the Reader interface.
     */
    public abstract Object newInstance(EntityInput input, boolean rawAccess)
        throws RefreshException;

    /**
     * Called after newInstance() to read the rest of the data bytes and fill
     * in the object contents.  If the object was read completely by
     * newInstance(), this method does nothing.
     *
     * Is public only in order to implement the Reader interface.  Note that
     * this method should only be called directly in raw conversion mode or
     * during conversion of an old format.  Normally it should be called via
     * the getReader method and the Reader interface.
     */
    public abstract Object readObject(Object o,
                                      EntityInput input,
                                      boolean rawAccess)
        throws RefreshException;

    /**
     * Writes a given instance of the target class to the output data bytes.
     * This is the complement of the newInstance()/readObject() pair.
     */
    abstract void writeObject(Object o, EntityOutput output, boolean rawAccess)
        throws RefreshException;

    /**
     * Skips over the object's contents, as if readObject() were called, but
     * without returning an object.  Used for extracting secondary key bytes
     * without having to instantiate the object.  For reference types, the
     * format ID is read just before calling this method, so this method is
     * responsible for skipping everything following the format ID.
     */
    abstract void skipContents(RecordInput input)
        throws RefreshException;

    /* -- More methods that may optionally be overridden by subclasses. -- */

    /**
     * When extracting a secondary key, called to skip over all fields up to
     * the given secondary key field.  Returns the format of the key field
     * found, or null if the field is not present (nullified) in the object.
     */
    Format skipToSecKey(RecordInput input, String keyName)
        throws RefreshException {

        throw DbCompat.unexpectedState(toString());
    }

    /**
     * Called after skipToSecKey() to copy the data bytes of a singular
     * (XXX_TO_ONE) key field.
     */
    void copySecKey(RecordInput input, RecordOutput output) {
        throw DbCompat.unexpectedState(toString());
    }

    /**
     * Called after skipToSecKey() to copy the data bytes of an array or
     * collection (XXX_TO_MANY) key field.
     */
    void copySecMultiKey(RecordInput input, Format keyFormat, Set results)
        throws RefreshException {

        throw DbCompat.unexpectedState(toString());
    }

    /**
     * Nullifies the given key field in the given RawObject --  rawAccess mode
     * is implied.
     */
    boolean nullifySecKey(Catalog catalog,
                          Object entity,
                          String keyName,
                          Object keyElement) {
        throw DbCompat.unexpectedState(toString());
    }

    /**
     * Returns whether the entity's primary key field is null or zero, as
     * defined for primary keys that are assigned from a sequence.
     */
    boolean isPriKeyNullOrZero(Object o, boolean rawAccess) {
        throw DbCompat.unexpectedState(toString());
    }

    /**
     * Gets the primary key field from the given object and writes it to the
     * given output data bytes.  This is a separate operation because the
     * primary key data bytes are stored separately from the rest of the
     * record.
     */
    void writePriKey(Object o, EntityOutput output, boolean rawAccess)
        throws RefreshException {

        throw DbCompat.unexpectedState(toString());
    }

    /**
     * Reads the primary key from the given input bytes and sets the primary
     * key field in the given object.  This is complement of writePriKey().
     *
     * Is public only in order to implement the Reader interface.  Note that
     * this method should only be called directly in raw conversion mode or
     * during conversion of an old format.  Normally it should be called via
     * the getReader method and the Reader interface.
     */
    public void readPriKey(Object o, EntityInput input, boolean rawAccess)
        throws RefreshException {

        throw DbCompat.unexpectedState(toString());
    }

    /**
     * For an entity class or subclass, returns the old key name for the given
     * key name that has been renamed, or returns the given key name if it has
     * not been renamed.
     */
    public String getOldKeyName(final String keyName) {
        throw DbCompat.unexpectedState(toString());
    }

    /**
     * Validates and returns the simple integer key format for a sequence key
     * associated with this format.
     *
     * For a composite key type, the format of the one and only field is
     * returned.  For a simple integer type, this format is returned.
     * Otherwise (the default implementation), an IllegalArgumentException is
     * thrown.
     */
    Format getSequenceKeyFormat() {
        throw new IllegalArgumentException
            ("Type not allowed for sequence: " + getClassName());
    }

    /**
     * Converts a RawObject to a current class object and adds the converted
     * pair to the converted map.
     */
    Object convertRawObject(Catalog catalog,
                            boolean rawAccess,
                            RawObject rawObject,
                            IdentityHashMap converted)
        throws RefreshException {

        throw DbCompat.unexpectedState(toString());
    }
   
    /**
     * Currently, only FBigDec will return true. It is a workaround for reading
     * the BigDecimal data stored by BigDecimal proxy before je4.1.
     */
    public boolean allowEvolveFromProxy() {
        return false;
    }
   
    public Accessor getAccessor(boolean rawAccess) {
        return null;
    }

    @Override
    public String toString() {
        final String INDENT = "  ";
        final String INDENT2 = INDENT + "  ";
        StringBuilder buf = new StringBuilder(500);
        if (isSimple()) {
            addTypeHeader(buf, "SimpleType");
            buf.append(" primitive=\"");
            buf.append(isPrimitive());
            buf.append("\"/>\n");
        } else if (isEnum()) {
            addTypeHeader(buf, "EnumType");
            buf.append(">\n");
            for (String constant : getEnumConstants()) {
                buf.append(INDENT);
                buf.append("<Constant>");
                buf.append(constant);
                buf.append("</Constant>\n");
            }
            buf.append("</EnumType>\n");
        } else if (isArray()) {
            addTypeHeader(buf, "ArrayType");
            buf.append(" componentId=\"");
            buf.append(getComponentType().getVersion());
            buf.append("\" componentClass=\"");
            buf.append(getComponentType().getClassName());
            buf.append("\" dimensions=\"");
            buf.append(getDimensions());
            buf.append("\"/>\n");
        } else {
            addTypeHeader(buf, "ComplexType");
            Format superType = getSuperType();
            if (superType != null) {
                buf.append(" superTypeId=\"");
                buf.append(superType.getId());
                buf.append("\" superTypeClass=\"");
                buf.append(superType.getClassName());
                buf.append('"');
            }
            Format proxiedFormat = getProxiedFormat();
            if (proxiedFormat != null) {
                buf.append(" proxiedTypeId=\"");
                buf.append(proxiedFormat.getId());
                buf.append("\" proxiedTypeClass=\"");
                buf.append(proxiedFormat.getClassName());
                buf.append('"');
            }
            PrimaryKeyMetadata priMeta = null;
            Map<String, SecondaryKeyMetadata> secondaryKeys = null;
            List<FieldMetadata> compositeKeyFields = null;
            ClassMetadata clsMeta = getClassMetadata();
            if (clsMeta != null) {
                compositeKeyFields = clsMeta.getCompositeKeyFields();
                priMeta = clsMeta.getPrimaryKey();
                secondaryKeys = clsMeta.getSecondaryKeys();
            }
            buf.append(" kind=\"");
            buf.append(isEntity() ? "entity" :
                       ((compositeKeyFields != null) ? "compositeKey" :
                        "persistent"));
            buf.append("\">\n");
            Map<String, RawField> fields = getFields();
            if (fields != null) {
                for (RawField field : fields.values()) {
                    String name = field.getName();
                    RawType type = field.getType();
                    buf.append(INDENT);
                    buf.append("<Field");
                    buf.append(" name=\"");
                    buf.append(name);
                    buf.append("\" typeId=\"");
                    buf.append(type.getId());
                    buf.append("\" typeClass=\"");
                    buf.append(type.getClassName());
                    buf.append('"');
                    if (priMeta != null &&
                        priMeta.getName().equals(name)) {
                        buf.append(" primaryKey=\"true\"");
                        if (priMeta.getSequenceName() != null) {
                            buf.append(" sequence=\"");
                            buf.append(priMeta.getSequenceName());
                            buf.append('"');
                        }
                    }
                    if (secondaryKeys != null) {
                        SecondaryKeyMetadata secMeta =
                            ComplexFormat.getSecondaryKeyMetadataByFieldName
                                (secondaryKeys, name);
                        if (secMeta != null) {
                            buf.append(" secondaryKey=\"true\" keyName=\"");
                            buf.append(secMeta.getKeyName());
                            buf.append("\" relate=\"");
                            buf.append(secMeta.getRelationship());
                            buf.append('"');
                            String related = secMeta.getRelatedEntity();
                            if (related != null) {
                                buf.append("\" relatedEntity=\"");
                                buf.append(related);
                                buf.append("\" onRelatedEntityDelete=\"");
                                buf.append(secMeta.getDeleteAction());
                                buf.append('"');
                            }
                        }
                    }
                    if (compositeKeyFields != null) {
                        int nFields = compositeKeyFields.size();
                        for (int i = 0; i < nFields; i += 1) {
                            FieldMetadata fldMeta = compositeKeyFields.get(i);
                            if (fldMeta.getName().equals(name)) {
                                buf.append(" compositeKeyField=\"");
                                buf.append(i + 1);
                                buf.append('"');
                            }
                        }
                    }
                    buf.append("/>\n");
                }
                EntityMetadata entMeta = getEntityMetadata();
                if (entMeta != null) {
                    buf.append(INDENT);
                    buf.append("<EntityKeys>\n");
                    priMeta = entMeta.getPrimaryKey();
                    if (priMeta != null) {
                        buf.append(INDENT2);
                        buf.append("<Primary class=\"");
                        buf.append(priMeta.getDeclaringClassName());
                        buf.append("\" field=\"");
                        buf.append(priMeta.getName());
                        buf.append("\"/>\n");
                    }
                    secondaryKeys = entMeta.getSecondaryKeys();
                    if (secondaryKeys != null) {
                        for (SecondaryKeyMetadata secMeta :
                             secondaryKeys.values()) {
                            buf.append(INDENT2);
                            buf.append("<Secondary class=\"");
                            buf.append(secMeta.getDeclaringClassName());
                            buf.append("\" field=\"");
                            buf.append(secMeta.getName());
                            buf.append("\"/>\n");
                        }
                    }
                    buf.append("</EntityKeys>\n");
                }
            }
            buf.append("</ComplexType>\n");
        }
        return buf.toString();
    }

    private void addTypeHeader(StringBuilder buf, String elemName) {
        buf.append('<');
        buf.append(elemName);
        buf.append(" id=\"");
        buf.append(getId());
        buf.append("\" class=\"");
        buf.append(getClassName());
        buf.append("\" version=\"");
        buf.append(getVersion());
        buf.append('"');
        Format currVersion = getLatestVersion();
        if (currVersion != null) {
            buf.append(" currentVersionId=\"");
            buf.append(currVersion.getId());
            buf.append('"');
        }
        Format prevVersion = getPreviousVersion();
        if (prevVersion != null) {
            buf.append(" previousVersionId=\"");
            buf.append(prevVersion.getId());
            buf.append('"');
        }
    }
}
TOP

Related Classes of com.sleepycat.persist.impl.Format

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.