All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sleepycat.persist.impl.Format Maven / Gradle / Ivy

The newest version!
/*-
 * Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
 *
 * This file was distributed by Oracle as part of a version of Oracle Berkeley
 * DB Java Edition made available at:
 *
 * http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
 *
 * Please see the LICENSE file included in the top-level directory of the
 * appropriate version of Oracle Berkeley DB Java Edition for a copy of the
 * license and additional information.
 */

package com.sleepycat.persist.impl;

import java.io.Serializable;
import java.util.HashSet;
import java.util.IdentityHashMap;
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;

/**
 * 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 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();
    }

    /**
     * Special handling for JE 3.0.12 beta formats.
     */
    void migrateFromBeta(Map 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 getEnumConstants() {
        return null;
    }

    public boolean isArray() {
        return false;
    }

    public int getDimensions() {
        return 0;
    }

    public Format getComponentType() {
        return null;
    }

    public Map 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.
     *
     * 

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.

* *

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.

*/ 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 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(""); buf.append(constant); buf.append("\n"); } buf.append("\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 secondaryKeys = null; List 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 fields = getFields(); if (fields != null) { for (RawField field : fields.values()) { String name = field.getName(); RawType type = field.getType(); buf.append(INDENT); buf.append("\n"); } EntityMetadata entMeta = getEntityMetadata(); if (entMeta != null) { buf.append(INDENT); buf.append("\n"); priMeta = entMeta.getPrimaryKey(); if (priMeta != null) { buf.append(INDENT2); buf.append("\n"); } secondaryKeys = entMeta.getSecondaryKeys(); if (secondaryKeys != null) { for (SecondaryKeyMetadata secMeta : secondaryKeys.values()) { buf.append(INDENT2); buf.append("\n"); } } buf.append("\n"); } } buf.append("\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('"'); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy