2
* See the file LICENSE for redistribution information.
4
* Copyright (c) 2002, 2010 Oracle and/or its affiliates. All rights reserved.
9
package com.sleepycat.persist.impl;
11
import java.io.Serializable;
12
import java.util.HashSet;
13
import java.util.IdentityHashMap;
14
import java.util.List;
18
import com.sleepycat.persist.evolve.Converter;
19
import com.sleepycat.persist.model.ClassMetadata;
20
import com.sleepycat.persist.model.EntityMetadata;
21
import com.sleepycat.persist.model.EntityModel;
22
import com.sleepycat.persist.model.FieldMetadata;
23
import com.sleepycat.persist.model.PrimaryKeyMetadata;
24
import com.sleepycat.persist.model.SecondaryKeyMetadata;
25
import com.sleepycat.persist.raw.RawField;
26
import com.sleepycat.persist.raw.RawObject;
27
import com.sleepycat.persist.raw.RawType;
30
* The base class for all object formats. Formats are used to define the
31
* stored layout for all persistent classes, including simple types.
33
* The design documentation below describes the storage format for entities and
34
* its relationship to information stored per format in the catalog.
38
* + Provides EntityBinding for objects and EntryBinding for keys.
39
* + Provides SecondaryKeyCreator, SecondaryMultiKeyCreator and
40
* SecondaryMultiKeyNullifier (SecondaryKeyNullifier is redundant).
41
* + Works with reflection and bytecode enhancement.
42
* + For reflection only, works with any entity model not just annotations.
43
* + Bindings are usable independently of the persist API.
44
* + Performance is almost equivalent to hand coded tuple bindings.
45
* + Small performance penalty for compatible class changes (new fields,
47
* + Secondary key create/nullify do not have to deserialize the entire record;
48
* in other words, store secondary keys at the start of the data.
52
* Every distinct class format is given a unique format ID. Class IDs are not
53
* equivalent to class version numbers (as in the version property of @Entity
54
* and @Persistent) because the format can change when the version number does
55
* not. Changes that cause a unique format ID to be assigned are:
59
* + Change primitive type to primitive wrapper class.
60
* + Add or drop secondary key.
61
* + Any incompatible class change.
63
* The last item, incompatible class changes, also correspond to a class
66
* For each distinct class format the following information is conceptually
67
* stored in the catalog, keyed by format ID.
70
* - Class version number
72
* - Kind: simple, enum, complex, array
73
* - For kind == simple:
76
* - Array of constant names, sorted by name.
77
* - For kind == complex:
78
* - Primary key fieldInfo, or null if no primary key is declared
79
* - Array of secondary key fieldInfo, sorted by field name
80
* - Array of other fieldInfo, sorted by field name
81
* - For kind == array:
82
* - Component class format
83
* - Number of array dimensions
84
* - Other metadata for RawType
89
* - Other metadata for RawField
93
* For each entity instance the data layout is as follows:
95
* instanceData: formatId keyFields... nonKeyFields...
96
* keyFields: fieldValue...
97
* nonKeyFields: fieldValue...
99
* The formatId is the (positive non-zero) ID of a class format, defined above.
100
* This is ID of the most derived class of the instance. It is stored as a
103
* Following the format ID, zero or more sets of secondary key field values
104
* appear, followed by zero or more sets of other class field values.
106
* The keyFields are the sets of secondary key fields for each class in order
107
* of the highest superclass first. Within a class, fields are ordered by
110
* The nonKeyFields are the sets of other non-key fields for each class in
111
* order of the highest superclass first. Within a class, fields are ordered
116
* fieldValue: primitiveValue
124
* For a primitive type, a primitive value is used as defined for tuple
125
* bindings. For float and double, sorted float and sorted double tuple values
128
* For a non-primitive type with a null value, a nullId is used that has a zero
129
* (illegal formatId) value. This includes String and other simple reference
130
* types. The formatId is stored as a packed integer, meaning that it is
131
* stored as a single zero byte.
133
* For a non-primitive type, an instanceRef is used for a non-null instance
134
* that appears earlier in the data byte array. An instanceRef is the negation
135
* of the byte offset of the instanceData that appears earlier. It is stored
136
* as a packed integer.
138
* The remaining rules apply only to reference types with non-null values that
139
* do not appear earlier in the data array.
141
* For an array type, an array formatId is used that identifies the component
142
* type and the number of array dimensions. This is followed by an array
143
* length (stored as a packed integer) and zero or more fieldValue elements.
144
* For an array with N+1 dimensions where N is greater than zero, the leftmost
145
* dimension is enumerated such that each fieldValue element is itself an array
146
* of N dimensions or null.
148
* arrayValue: formatId length fieldValue...
150
* For an enum type, an enumValue is used, consisting of a formatId that
151
* identifies the enum class and an enumIndex (stored as a packed integer) that
152
* identifies the constant name in the enum constant array of the enum class
155
* enumValue: formatId enumIndex
157
* For a simple type, a simpleValue is used. This consists of the formatId
158
* that identifies the class followed by the simple type value. For a
159
* primitive wrapper type the simple type value is the corresponding primitive,
160
* for a Date it is the milliseconds as a long primitive, and for BigInteger or
161
* BigDecimal it is a byte array as defined for tuple bindings of these types.
163
* simpleValue: formatId value
165
* For all other complex types, an instanceData is used, which is defined
170
* For secondary key support we must account for writing and nullifying
171
* specific keys. Rather than instantiating the entity and then performing
172
* the secondary key operation, we strive to perform the secondary key
173
* operation directly on the byte format.
175
* To create a secondary key we skip over other fields and then copy the bytes
176
* of the embedded key. This approach is very efficient because a) the entity
177
* is not instantiated, and b) the secondary keys are stored at the beginning
178
* of the byte format and can be quickly read.
180
* To nullify we currently instantiate the raw entity, set the key field to null
181
* (or remove it from the array/collection), and convert the raw entity back to
182
* bytes. Although the performance of this approach is not ideal because it
183
* requires serialization, it avoids the complexity of modifying the packed
184
* serialized format directly, adjusting references to key objects, etc. Plus,
185
* when we nullify a key we are going to write the record, so the serialization
186
* overhead may not be significant. For the record, I tried implementing
187
* nullification of the bytes directly and found it was much too complex.
191
* Format are managed by a Catalog class. Simple formats are managed by
192
* SimpleCatalog, and are copied from the SimpleCatalog by PersistCatalog.
193
* Other formats are managed by PersistCatalog. The lifecycle of a format
196
* - Constructed by the catalog when a format is requested for a Class
197
* that currently has no associated format.
199
* - The catalog calls setId() and adds the format to its format list
200
* (indexed by format id) and map (keyed by class name).
202
* - The catalog calls collectRelatedFormats(), where a format can create
203
* additional formats that it needs, or that should also be persistent.
205
* - The catalog calls initializeIfNeeded(), which calls the initialize()
206
* method of the format class.
208
* - initialize() should initialize any transient fields in the format.
209
* initialize() can assume that all related formats are available in the
210
* catalog. It may call initializeIfNeeded() for those related formats, if
211
* it needs to interact with an initialized related format; this does not
212
* cause a cycle, because initializeIfNeeded() does nothing for an already
213
* initialized format.
215
* - The catalog creates a group of related formats at one time, and then
216
* writes its entire list of formats to the catalog DB as a single record.
217
* This grouping reduces the number of writes.
219
* - When a catalog is opened and the list of existing formats is read. After
220
* a format is deserialized, its initializeIfNeeded() method is called.
221
* setId() and collectRelatedFormats() are not called, since the ID and
222
* related formats are stored in serialized fields.
224
* - There are two modes for opening an existing catalog: raw mode and normal
225
* mode. In raw mode, the old format is used regardless of whether it
226
* matches the current class definition; in fact the class is not accessed
227
* and does not need to be present.
229
* - In normal mode, for each existing format that is initialized, a new format
230
* is also created based on the current class and metadata definition. If
231
* the two formats are equal, the new format is discarded. If they are
232
* unequal, the new format becomes the current format and the old format's
233
* evolve() method is called. evolve() is responsible for adjusting the
234
* old format for class evolution. Any number of non-current formats may
235
* exist for a given class, and are setup to evolve the single current format
240
public abstract class Format implements Reader, RawType, Serializable {
242
private static final long serialVersionUID = 545633644568489850L;
244
/** Null reference. */
245
static final int ID_NULL = 0;
247
static final int ID_OBJECT = 1;
249
static final int ID_BOOL = 2;
250
static final int ID_BOOL_W = 3;
252
static final int ID_BYTE = 4;
253
static final int ID_BYTE_W = 5;
255
static final int ID_SHORT = 6;
256
static final int ID_SHORT_W = 7;
258
static final int ID_INT = 8;
259
static final int ID_INT_W = 9;
261
static final int ID_LONG = 10;
262
static final int ID_LONG_W = 11;
264
static final int ID_FLOAT = 12;
265
static final int ID_FLOAT_W = 13;
267
static final int ID_DOUBLE = 14;
268
static final int ID_DOUBLE_W = 15;
270
static final int ID_CHAR = 16;
271
static final int ID_CHAR_W = 17;
273
static final int ID_STRING = 18;
275
static final int ID_BIGINT = 19;
277
static final int ID_BIGDEC = 20;
279
static final int ID_DATE = 21;
281
static final int ID_NUMBER = 22;
283
/** First simple type. */
284
static final int ID_SIMPLE_MIN = 2;
285
/** Last simple type. */
286
static final int ID_SIMPLE_MAX = 21;
287
/** Last predefined ID, after which dynamic IDs are assigned. */
288
static final int ID_PREDEFINED = 30;
290
static boolean isPredefined(Format format) {
291
return format.getId() <= ID_PREDEFINED;
295
private String className;
296
private Reader reader;
297
private Format superFormat;
298
private Format latestFormat;
299
private Format previousFormat;
300
private Set<String> supertypes;
301
private boolean deleted;
302
private boolean unused;
303
private transient Catalog catalog;
304
private transient Class type;
305
private transient Format proxiedFormat;
306
private transient boolean initialized;
309
* Creates a new format for a given class.
312
this(type.getName());
318
* Creates a format for class evolution when no class may be present.
320
Format(String className) {
321
this.className = className;
323
supertypes = new HashSet<String>();
327
* Special handling for JE 3.0.12 beta formats.
329
void migrateFromBeta(Map<String,Format> formatMap) {
330
if (latestFormat == null) {
335
final boolean isNew() {
339
final Catalog getCatalog() {
344
* Returns the format ID.
346
public final int getId() {
351
* Called by the Catalog to set the format ID when a new format is added to
352
* the format list, before calling initializeIfNeeded().
354
final void setId(int id) {
359
* Returns the class that this format represents. This method will return
360
* null in rawAccess mode, or for an unevolved format.
362
final Class getType() {
367
* Called to get the type when it is known to exist for an uninitialized
370
final Class getExistingType() {
373
type = SimpleCatalog.classForName(className);
374
} catch (ClassNotFoundException e) {
375
throw new IllegalStateException(e);
382
* Returns the object for reading objects of the latest format. For the
383
* latest version format, 'this' is returned. For prior version formats, a
384
* reader that converts this version to the latest version is returned.
386
final Reader getReader() {
389
* For unit testing, record whether any un-evolved formats are
392
if (this != reader) {
393
PersistCatalog.unevolvedFormatsEncountered = true;
400
* Changes the reader during format evolution.
402
final void setReader(Reader reader) {
403
this.reader = reader;
407
* Returns the format of the superclass.
409
final Format getSuperFormat() {
414
* Called to set the format of the superclass during initialize().
416
final void setSuperFormat(Format superFormat) {
417
this.superFormat = superFormat;
421
* Returns the format that is proxied by this format. If non-null is
422
* returned, then this format is a PersistentProxy.
424
final Format getProxiedFormat() {
425
return proxiedFormat;
429
* Called by ProxiedFormat to set the proxied format.
431
final void setProxiedFormat(Format proxiedFormat) {
432
this.proxiedFormat = proxiedFormat;
436
* If this is the latest/evolved format, returns this; otherwise, returns
437
* the current version of this format. Note that this WILL return a
438
* format for a deleted class if the latest format happens to be deleted.
440
final Format getLatestVersion() {
445
* Returns the previous version of this format in the linked list of
446
* versions, or null if this is the only version.
448
public final Format getPreviousVersion() {
449
return previousFormat;
453
* Called by Evolver to set the latest format when this old format is
456
final void setLatestVersion(Format newFormat) {
459
* If this old format is the former latest version, link it to the new
460
* latest version. This creates a singly linked list of versions
461
* starting with the latest.
463
if (latestFormat == this) {
464
newFormat.previousFormat = this;
467
latestFormat = newFormat;
471
* Returns whether the class for this format was deleted.
473
final boolean isDeleted() {
478
* Called by the Evolver when applying a Deleter mutation.
480
final void setDeleted(boolean deleted) {
481
this.deleted = deleted;
485
* Called by the Evolver for a format that is never referenced.
487
final void setUnused(boolean unused) {
488
this.unused = unused;
492
* Called by the Evolver with true when an entity format or any of its
493
* nested format were changed. Called by Store.evolve when an entity has
494
* been fully converted. Overridden by ComplexFormat.
496
void setEvolveNeeded(boolean needed) {
497
throw new UnsupportedOperationException();
501
* Overridden by ComplexFormat.
503
boolean getEvolveNeeded() {
504
throw new UnsupportedOperationException();
507
final boolean isInitialized() {
512
* Called by the Catalog to initialize a format, and may also be called
513
* during initialize() for a related format to ensure that the related
514
* format is initialized. This latter case is allowed to support
515
* bidirectional dependencies. This method will do nothing if the format
516
* is already intialized.
518
final void initializeIfNeeded(Catalog catalog, EntityModel model) {
521
this.catalog = catalog;
523
/* Initialize objects serialized by an older Format class. */
524
if (latestFormat == null) {
527
if (reader == null) {
532
* The class is only guaranteed to be available in live (not raw)
533
* mode, for the current version of the format.
536
isCurrentVersion() &&
537
(isSimple() || !catalog.isRawAccess())) {
541
/* Perform subclass-specific initialization. */
542
initialize(catalog, model,
543
catalog.getInitVersion(this, false /*forReader*/));
544
reader.initializeReader
546
catalog.getInitVersion(this, true /*forReader*/),
552
* Called to initialize a separate Reader implementation. This method is
553
* called when no separate Reader exists, and does nothing.
555
public void initializeReader(Catalog catalog,
562
* Adds all interfaces and superclasses to the supertypes set.
564
private void addSupertypes() {
566
Class stype = type.getSuperclass();
567
while (stype != null && stype != Object.class) {
568
supertypes.add(stype.getName());
569
addInterfaces(stype);
570
stype = stype.getSuperclass();
575
* Recursively adds interfaces to the supertypes set.
577
private void addInterfaces(Class cls) {
578
Class[] interfaces = cls.getInterfaces();
579
for (Class iface : interfaces) {
580
if (iface != Enhanced.class) {
581
supertypes.add(iface.getName());
582
addInterfaces(iface);
588
* Certain formats (ProxiedFormat for example) prohibit nested fields that
589
* reference the parent object. [#15815]
591
boolean areNestedRefsProhibited() {
595
/* -- Start of RawType interface methods. -- */
597
public String getClassName() {
601
public int getVersion() {
602
ClassMetadata meta = getClassMetadata();
604
return meta.getVersion();
610
public Format getSuperType() {
614
/* -- RawType methods that are overridden as needed in subclasses. -- */
616
public boolean isSimple() {
620
public boolean isPrimitive() {
624
public boolean isEnum() {
628
public List<String> getEnumConstants() {
632
public boolean isArray() {
636
public int getDimensions() {
640
public Format getComponentType() {
644
public Map<String,RawField> getFields() {
648
public ClassMetadata getClassMetadata() {
652
public EntityMetadata getEntityMetadata() {
656
/* -- End of RawType methods. -- */
658
/* -- Methods that may optionally be overridden by subclasses. -- */
661
* Called by EntityOutput in rawAccess mode to determine whether an object
662
* type is allowed to be assigned to a given field type.
664
boolean isAssignableTo(Format format) {
665
if (proxiedFormat != null) {
666
return proxiedFormat.isAssignableTo(format);
668
return format == this ||
669
format.id == ID_OBJECT ||
670
supertypes.contains(format.className);
675
* For primitive types only, returns their associated wrapper type.
677
Format getWrapperFormat() {
682
* Returns whether this format class is an entity class.
689
* Returns whether this class is present in the EntityModel. Returns false
690
* for a simple type, array type, or enum type.
692
boolean isModelClass() {
697
* For an entity class or subclass, returns the base entity class; returns
698
* null in other cases.
700
ComplexFormat getEntityFormat() {
705
* Called for an existing format that may not equal the current format for
708
* <p>If this method returns true, then it must have determined one of two
710
* - that the old and new formats are equal, and it must have called
711
* Evolver.useOldFormat; or
712
* - that the old format can be evolved to the new format, and it must
713
* have called Evolver.useEvolvedFormat.</p>
715
* <p>If this method returns false, then it must have determined that the
716
* old format could not be evolved to the new format, and it must have
717
* called Evolver.addInvalidMutation, addMissingMutation or
718
* addEvolveError.</p>
720
abstract boolean evolve(Format newFormat, Evolver evolver);
723
* Called when a Converter handles evolution of a class, but we may still
724
* need to evolve the metadata.
726
boolean evolveMetadata(Format newFormat,
733
* Returns whether this format is the current format for its class. If
734
* false is returned, this format is setup to evolve to the current format.
736
final boolean isCurrentVersion() {
737
return latestFormat == this && !deleted;
741
* Returns whether this format has the same class as the given format,
742
* irrespective of version changes and renaming.
744
final boolean isSameClass(Format other) {
745
return latestFormat == other.latestFormat;
748
/* -- Abstract methods that must be implemented by subclasses. -- */
751
* Initializes an uninitialized format, initializing its related formats
752
* (superclass formats and array component formats) first.
754
abstract void initialize(Catalog catalog,
759
* Calls catalog.createFormat for formats that this format depends on, or
760
* that should also be persistent.
762
abstract void collectRelatedFormats(Catalog catalog,
763
Map<String,Format> newFormats);
766
* The remaining methods are used to read objects from data bytes via
767
* EntityInput, and to write objects as data bytes via EntityOutput.
768
* Ultimately these methods call methods in the Accessor interface to
769
* get/set fields in the object. Most methods have a rawAccess parameter
770
* that determines whether the object is a raw object or a real persistent
773
* The first group of methods are abstract and must be implemented by
774
* format classes. The second group have default implementations that
775
* throw UnsupportedOperationException and may optionally be overridden.
779
* Creates an array of the format's class of the given length, as if
780
* Array.newInstance(getType(), len) were called. Formats implement this
781
* method for specific classes, or call the accessor, to avoid the
782
* reflection overhead of Array.newInstance.
784
abstract Object newArray(int len);
787
* Creates a new instance of the target class using its default
788
* constructor. Normally this creates an empty object, and readObject() is
789
* called next to fill in the contents. This is done in two steps to allow
790
* the instance to be registered by EntityInput before reading the
791
* contents. This allows the fields in an object or a nested object to
792
* refer to the parent object in a graph.
794
* Alternatively, this method may read all or the first portion of the
795
* data, rather than that being done by readObject(). This is required for
796
* simple types and enums, where the object cannot be created without
797
* reading the data. In these cases, there is no possibility that the
798
* parent object will be referenced by the child object in the graph. It
799
* should not be done in other cases, or the graph references may not be
800
* maintained faithfully.
802
* Is public only in order to implement the Reader interface. Note that
803
* this method should only be called directly in raw conversion mode or
804
* during conversion of an old format. Normally it should be called via
805
* the getReader method and the Reader interface.
807
public abstract Object newInstance(EntityInput input, boolean rawAccess);
810
* Called after newInstance() to read the rest of the data bytes and fill
811
* in the object contents. If the object was read completely by
812
* newInstance(), this method does nothing.
814
* Is public only in order to implement the Reader interface. Note that
815
* this method should only be called directly in raw conversion mode or
816
* during conversion of an old format. Normally it should be called via
817
* the getReader method and the Reader interface.
819
public abstract Object readObject(Object o,
824
* Writes a given instance of the target class to the output data bytes.
825
* This is the complement of the newInstance()/readObject() pair.
827
abstract void writeObject(Object o, EntityOutput output, boolean rawAccess);
830
* Skips over the object's contents, as if readObject() were called, but
831
* without returning an object. Used for extracting secondary key bytes
832
* without having to instantiate the object. For reference types, the
833
* format ID is read just before calling this method, so this method is
834
* responsible for skipping everything following the format ID.
836
abstract void skipContents(RecordInput input);
838
/* -- More methods that may optionally be overridden by subclasses. -- */
841
* When extracting a secondary key, called to skip over all fields up to
842
* the given secondary key field. Returns the format of the key field
843
* found, or null if the field is not present (nullified) in the object.
845
Format skipToSecKey(RecordInput input, String keyName) {
846
throw new UnsupportedOperationException(toString());
850
* Called after skipToSecKey() to copy the data bytes of a singular
851
* (XXX_TO_ONE) key field.
853
void copySecKey(RecordInput input, RecordOutput output) {
854
throw new UnsupportedOperationException(toString());
858
* Called after skipToSecKey() to copy the data bytes of an array or
859
* collection (XXX_TO_MANY) key field.
861
void copySecMultiKey(RecordInput input, Format keyFormat, Set results) {
862
throw new UnsupportedOperationException(toString());
866
* Nullifies the given key field in the given RawObject -- rawAccess mode
869
boolean nullifySecKey(Catalog catalog,
873
throw new UnsupportedOperationException(toString());
877
* Returns whether the entity's primary key field is null or zero, as
878
* defined for primary keys that are assigned from a sequence.
880
boolean isPriKeyNullOrZero(Object o, boolean rawAccess) {
881
throw new UnsupportedOperationException(toString());
885
* Gets the primary key field from the given object and writes it to the
886
* given output data bytes. This is a separate operation because the
887
* primary key data bytes are stored separately from the rest of the
890
void writePriKey(Object o, EntityOutput output, boolean rawAccess) {
891
throw new UnsupportedOperationException(toString());
895
* Reads the primary key from the given input bytes and sets the primary
896
* key field in the given object. This is complement of writePriKey().
898
* Is public only in order to implement the Reader interface. Note that
899
* this method should only be called directly in raw conversion mode or
900
* during conversion of an old format. Normally it should be called via
901
* the getReader method and the Reader interface.
903
public void readPriKey(Object o, EntityInput input, boolean rawAccess) {
904
throw new UnsupportedOperationException(toString());
908
* Validates and returns the simple integer key format for a sequence key
909
* associated with this format.
911
* For a composite key type, the format of the one and only field is
912
* returned. For a simple integer type, this format is returned.
913
* Otherwise (the default implementation), an IllegalArgumentException is
916
Format getSequenceKeyFormat() {
917
throw new IllegalArgumentException
918
("Type not allowed for sequence: " + getClassName());
922
* Converts a RawObject to a current class object and adds the converted
923
* pair to the converted map.
925
Object convertRawObject(Catalog catalog,
928
IdentityHashMap converted) {
929
throw new UnsupportedOperationException(toString());
933
public String toString() {
934
final String INDENT = " ";
935
final String INDENT2 = INDENT + " ";
936
StringBuffer buf = new StringBuffer(500);
938
addTypeHeader(buf, "SimpleType");
939
buf.append(" primitive=\"");
940
buf.append(isPrimitive());
941
buf.append("\"/>\n");
942
} else if (isEnum()) {
943
addTypeHeader(buf, "EnumType");
945
for (String constant : getEnumConstants()) {
947
buf.append("<Constant>");
948
buf.append(constant);
949
buf.append("</Constant>\n");
951
buf.append("</EnumType>\n");
952
} else if (isArray()) {
953
addTypeHeader(buf, "ArrayType");
954
buf.append(" componentId=\"");
955
buf.append(getComponentType().getVersion());
956
buf.append("\" componentClass=\"");
957
buf.append(getComponentType().getClassName());
958
buf.append("\" dimensions=\"");
959
buf.append(getDimensions());
960
buf.append("\"/>\n");
962
addTypeHeader(buf, "ComplexType");
963
Format superType = getSuperType();
964
if (superType != null) {
965
buf.append(" superTypeId=\"");
966
buf.append(superType.getId());
967
buf.append("\" superTypeClass=\"");
968
buf.append(superType.getClassName());
971
Format proxiedFormat = getProxiedFormat();
972
if (proxiedFormat != null) {
973
buf.append(" proxiedTypeId=\"");
974
buf.append(proxiedFormat.getId());
975
buf.append("\" proxiedTypeClass=\"");
976
buf.append(proxiedFormat.getClassName());
979
PrimaryKeyMetadata priMeta = null;
980
Map<String,SecondaryKeyMetadata> secondaryKeys = null;
981
List<FieldMetadata> compositeKeyFields = null;
982
ClassMetadata clsMeta = getClassMetadata();
983
if (clsMeta != null) {
984
compositeKeyFields = clsMeta.getCompositeKeyFields();
985
priMeta = clsMeta.getPrimaryKey();
986
secondaryKeys = clsMeta.getSecondaryKeys();
988
buf.append(" kind=\"");
989
buf.append(isEntity() ? "entity" :
990
((compositeKeyFields != null) ? "compositeKey" :
993
Map<String, RawField> fields = getFields();
994
if (fields != null) {
995
for (RawField field : fields.values()) {
996
String name = field.getName();
997
RawType type = field.getType();
999
buf.append("<Field");
1000
buf.append(" name=\"");
1002
buf.append("\" typeId=\"");
1003
buf.append(type.getId());
1004
buf.append("\" typeClass=\"");
1005
buf.append(type.getClassName());
1007
if (priMeta != null &&
1008
priMeta.getName().equals(name)) {
1009
buf.append(" primaryKey=\"true\"");
1010
if (priMeta.getSequenceName() != null) {
1011
buf.append(" sequence=\"");
1012
buf.append(priMeta.getSequenceName());
1016
if (secondaryKeys != null) {
1017
SecondaryKeyMetadata secMeta =
1018
ComplexFormat.getSecondaryKeyMetadataByFieldName
1019
(secondaryKeys, name);
1020
if (secMeta != null) {
1021
buf.append(" secondaryKey=\"true\" keyName=\"");
1022
buf.append(secMeta.getKeyName());
1023
buf.append("\" relate=\"");
1024
buf.append(secMeta.getRelationship());
1026
String related = secMeta.getRelatedEntity();
1027
if (related != null) {
1028
buf.append("\" relatedEntity=\"");
1029
buf.append(related);
1030
buf.append("\" onRelatedEntityDelete=\"");
1031
buf.append(secMeta.getDeleteAction());
1036
if (compositeKeyFields != null) {
1037
int nFields = compositeKeyFields.size();
1038
for (int i = 0; i < nFields; i += 1) {
1039
FieldMetadata fldMeta = compositeKeyFields.get(i);
1040
if (fldMeta.getName().equals(name)) {
1041
buf.append(" compositeKeyField=\"");
1049
EntityMetadata entMeta = getEntityMetadata();
1050
if (entMeta != null) {
1052
buf.append("<EntityKeys>\n");
1053
priMeta = entMeta.getPrimaryKey();
1054
if (priMeta != null) {
1055
buf.append(INDENT2);
1056
buf.append("<Primary class=\"");
1057
buf.append(priMeta.getDeclaringClassName());
1058
buf.append("\" field=\"");
1059
buf.append(priMeta.getName());
1060
buf.append("\"/>\n");
1062
secondaryKeys = entMeta.getSecondaryKeys();
1063
if (secondaryKeys != null) {
1064
for (SecondaryKeyMetadata secMeta :
1065
secondaryKeys.values()) {
1066
buf.append(INDENT2);
1067
buf.append("<Secondary class=\"");
1068
buf.append(secMeta.getDeclaringClassName());
1069
buf.append("\" field=\"");
1070
buf.append(secMeta.getName());
1071
buf.append("\"/>\n");
1074
buf.append("</EntityKeys>\n");
1077
buf.append("</ComplexType>\n");
1079
return buf.toString();
1082
private void addTypeHeader(StringBuffer buf, String elemName) {
1084
buf.append(elemName);
1085
buf.append(" id=\"");
1086
buf.append(getId());
1087
buf.append("\" class=\"");
1088
buf.append(getClassName());
1089
buf.append("\" version=\"");
1090
buf.append(getVersion());
1092
Format currVersion = getLatestVersion();
1093
if (currVersion != null) {
1094
buf.append(" currentVersionId=\"");
1095
buf.append(currVersion.getId());
1098
Format prevVersion = getPreviousVersion();
1099
if (prevVersion != null) {
1100
buf.append(" previousVersionId=\"");
1101
buf.append(prevVersion.getId());