0001: /**
0002: * Objective Database Abstraction Layer (ODAL)
0003: * Copyright (c) 2004, The ODAL Development Group
0004: * All rights reserved.
0005: * For definition of the ODAL Development Group please refer to LICENCE.txt file
0006: *
0007: * Distributable under LGPL license.
0008: * See terms of license at gnu.org.
0009: */package com.completex.objective.components.persistency;
0010:
0011: import com.completex.objective.components.persistency.core.impl.LinkDependencyGraph;
0012: import com.completex.objective.components.persistency.core.impl.LinkIterator;
0013: import com.completex.objective.components.persistency.core.impl.query.QueryContext;
0014: import com.completex.objective.components.persistency.transact.Transaction;
0015: import com.completex.objective.components.persistency.type.BlobImpl;
0016: import com.completex.objective.components.persistency.type.ClobImpl;
0017:
0018: import java.io.*;
0019: import java.sql.Blob;
0020: import java.sql.Clob;
0021: import java.sql.SQLException;
0022: import java.util.*;
0023:
0024: /**
0025: * Representation of linked table structure. Each record is mapped to database table and optionally
0026: * has links to another records.
0027: *
0028: * @author Gennady Krizhevsky
0029: */
0030: public class Record implements Cloneable {
0031: public static final DefaultToParametersPolicy DEFAULT_TO_PARAMETERS_POLICY = new DefaultToParametersPolicy();
0032: public static final NotNullToParametersPolicy NOT_NULL_TO_PARAMETERS_POLICY = new NotNullToParametersPolicy();
0033:
0034: public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
0035:
0036: static final long serialVersionUID = 1L;
0037: private MetaTable table;
0038: private String tableName;
0039: private String tableAlias;
0040: private PersistentEntry[] entries;
0041: private State state = State.NEW;
0042: private boolean hasDirtyNonKeyFields;
0043: private boolean skipInsertForKeysOnly;
0044: private boolean autoPadChars;
0045: private boolean notOverwriteWithNulls;
0046: private LinkDependencyGraph dependencyGraph;
0047: private Link link;
0048: private boolean preserveOriginalValues;
0049: private transient boolean forceModify;
0050: private PersistentObject persistentObject;
0051:
0052: /**
0053: * @param table
0054: */
0055: public Record(MetaTable table) {
0056: this (table, false);
0057: }
0058:
0059: /**
0060: * @param table
0061: * @param skipInsertForKeysOnly if true records with only primary columnName values modified will not be persisted
0062: */
0063: public Record(MetaTable table, boolean skipInsertForKeysOnly) {
0064: this (table, skipInsertForKeysOnly, false);
0065: }
0066:
0067: /**
0068: * @param table
0069: * @param skipInsertForKeysOnly if true records with only primary columnName values modified will not be persisted
0070: * @param autoPadChars if true char fields will be right padded with spaces to column length
0071: */
0072: public Record(MetaTable table, boolean skipInsertForKeysOnly,
0073: boolean autoPadChars) {
0074: setupNewRecord(table, autoPadChars, skipInsertForKeysOnly);
0075: }
0076:
0077: private void setupNewRecord(MetaTable table, boolean autoPadChars,
0078: boolean skipInsertForKeysOnly) {
0079: if (table == null) {
0080: throw new RuntimeException("table may not be empty !!!");
0081: }
0082: this .table = table;
0083: setTableName(null);
0084: this .entries = new PersistentEntry[table.size()];
0085: for (int i = 0; i < this .entries.length; i++) {
0086: this .entries[i] = new PersistentEntry(table.getColumn(i),
0087: this );
0088: this .entries[i].setAutoPadChars(autoPadChars);
0089: }
0090: this .skipInsertForKeysOnly = skipInsertForKeysOnly;
0091: this .autoPadChars = autoPadChars;
0092: createLinkIfNotExist();
0093: }
0094:
0095: private Link createLinkIfNotExist() {
0096: if (link == null) {
0097: link = new Link(null, null, null);
0098: }
0099: return link;
0100: }
0101:
0102: /**
0103: * Adds persistent entry using the parameter one as a template (entry is deep cloned)
0104: *
0105: * @param entry
0106: */
0107: public void addPersistentEntry(PersistentEntry entry) {
0108: addPersistentEntry(null, entry);
0109: }
0110:
0111: /**
0112: * Adds persistent entry using the parameter one as a template
0113: *
0114: * @param newColumnName if not null, sets a new column name for added entry
0115: * @param entry
0116: */
0117: public void addPersistentEntry(String newColumnName,
0118: PersistentEntry entry) {
0119: addPersistentEntry(newColumnName, entry, true);
0120: }
0121:
0122: /**
0123: * Adds persistent entry using the parameter one as a template
0124: *
0125: * @param newColumnName if not null, sets a new column name for added entry
0126: * @param entry
0127: * @param cloneEntry if true added entry deep cloned before adding, otherwise it is added as is
0128: */
0129: protected void addPersistentEntry(String newColumnName,
0130: PersistentEntry entry, boolean cloneEntry) {
0131: if (table.isStaticFinal()) {
0132: MetaTable clonedTable = table.cloneDeepSafe();
0133: clonedTable.setStaticFinal(false);
0134: table = clonedTable;
0135: }
0136: int newSize = entries.length + 1;
0137: int newLastIndex = newSize - 1;
0138:
0139: MetaColumn column = entry.getColumn().cloneSafe();
0140: column.setColumnIndex(newLastIndex);
0141: if (newColumnName != null) {
0142: column.setColumnName(newColumnName);
0143: }
0144:
0145: table.addColumn(column);
0146: PersistentEntry[] newEntries = new PersistentEntry[newSize];
0147: System.arraycopy(entries, 0, newEntries, 0, entries.length);
0148: if (cloneEntry) {
0149: newEntries[newLastIndex] = entry.cloneDeepSafe();
0150: } else {
0151: newEntries[newLastIndex] = entry;
0152: }
0153: for (int i = 0; i < newEntries.length; i++) {
0154: PersistentEntry newEntry = newEntries[i];
0155: newEntry.setRecord(this );
0156: }
0157:
0158: entries = newEntries;
0159: }
0160:
0161: /**
0162: * Sets parent <code>PersistentObject</code>
0163: *
0164: * @param persistentObject
0165: */
0166: void setPersistentObject(PersistentObject persistentObject) {
0167: this .persistentObject = persistentObject;
0168: }
0169:
0170: /**
0171: * Gets parent <code>PersistentObject</code>
0172: *
0173: * @return parent PersistentObject
0174: */
0175: public PersistentObject getPersistentObject() {
0176: return persistentObject;
0177: }
0178:
0179: /**
0180: * Returns <tt>Link</tt> reperesentation of this Record
0181: *
0182: * @return Link reperesentation of this Record
0183: */
0184: public Link toLink() {
0185: return link;
0186: }
0187:
0188: /**
0189: * Gets <tt>LinkDependencyGraph</tt>
0190: *
0191: * @return LinkDependencyGraph
0192: */
0193: public LinkDependencyGraph getDependencyGraph() {
0194: return dependencyGraph;
0195: }
0196:
0197: /**
0198: * Sets <tt>LinkDependencyGraph</tt>
0199: *
0200: * @param dependencyGraph
0201: * @see LinkDependencyGraph
0202: */
0203: public void setDependencyGraph(LinkDependencyGraph dependencyGraph) {
0204: this .dependencyGraph = dependencyGraph;
0205: }
0206:
0207: /**
0208: * Assembles <tt>LinkDependencyGraph</tt>
0209: *
0210: * @param persistentObject
0211: */
0212: void assemble(AbstractPersistentObject persistentObject) {
0213: if (dependencyGraph != null) {
0214: dependencyGraph.assemble(persistentObject);
0215: }
0216: }
0217:
0218: /**
0219: * Returns <tt>MetaTable</tt> associated with this Record
0220: *
0221: * @return MetaTable associated with this Record
0222: */
0223: public MetaTable getTable() {
0224: return table;
0225: }
0226:
0227: /**
0228: * Returns array of persistent entries
0229: *
0230: * @return PersistentEntry[] - array of persistent entries
0231: * @see PersistentEntry
0232: */
0233: protected PersistentEntry[] getEntries() {
0234: return entries;
0235: }
0236:
0237: /**
0238: * @param state
0239: * @see State
0240: */
0241: public void setState(State state) {
0242: this .state = state;
0243: }
0244:
0245: /**
0246: * @return Record state
0247: * @see State
0248: */
0249: public State getState() {
0250: return state;
0251: }
0252:
0253: /**
0254: * @return true if Record has dirty non columnName fields
0255: */
0256: public boolean hasDirtyNonKeyFields() {
0257: return hasDirtyNonKeyFields;
0258: }
0259:
0260: /**
0261: * @param hasDirtyNonKeyFields
0262: */
0263: public void setHasDirtyNonKeyFields(boolean hasDirtyNonKeyFields) {
0264: this .hasDirtyNonKeyFields = hasDirtyNonKeyFields;
0265: }
0266:
0267: /**
0268: * @return true if the Record will not be persisted in case of columnName only fields are populated
0269: */
0270: public boolean isSkipInsertForKeysOnly() {
0271: return skipInsertForKeysOnly;
0272: }
0273:
0274: /**
0275: * @param skipInsertForKeysOnly
0276: */
0277: public void setSkipInsertForKeysOnly(boolean skipInsertForKeysOnly) {
0278: this .skipInsertForKeysOnly = skipInsertForKeysOnly;
0279: }
0280:
0281: /**
0282: * @return if char fields will be right padded with spaces to column length
0283: */
0284: public boolean isAutoPadChars() {
0285: return autoPadChars;
0286: }
0287:
0288: /**
0289: * Sets autoPadChars flag.
0290: * If set to true, character fields will be right padded with spaces to column length
0291: *
0292: * @param autoPadChars if true char fields will be right padded with spaces to column length
0293: */
0294: public void setAutoPadChars(boolean autoPadChars) {
0295: this .autoPadChars = autoPadChars;
0296: }
0297:
0298: /**
0299: * @return if original values will be serialized when parent persistent object
0300: * gets serialized though Externalizeable interface methods
0301: */
0302: public boolean isPreserveOriginalValues() {
0303: return preserveOriginalValues;
0304: }
0305:
0306: /**
0307: * @param preserveOriginalValues if true original values will be serialized when parent persistent object
0308: * gets serialized though Externalizeable interface methods
0309: */
0310: public void setPreserveOriginalValues(boolean preserveOriginalValues) {
0311: this .preserveOriginalValues = preserveOriginalValues;
0312: }
0313:
0314: //
0315: // Getters/Setter by columnName:
0316: //
0317:
0318: /**
0319: * @param columnName
0320: * @return true is value is null
0321: */
0322: public boolean isNull(String columnName) {
0323: return getObject(columnName) == null;
0324: }
0325:
0326: /**
0327: * @param columnName
0328: * @return field value as Number
0329: */
0330: public Number getNumber(String columnName) {
0331: Number value = null;
0332: try {
0333: value = (Number) getObject(columnName);
0334: } catch (ClassCastException e) {
0335: handleClassCastException(e, value, "Number");
0336: }
0337: return value;
0338: }
0339:
0340: /**
0341: * Returns field value as Boolean
0342: *
0343: * @param columnName
0344: * @return field value as Boolean
0345: */
0346: public Boolean getBoolean(String columnName) {
0347: Boolean value = null;
0348: try {
0349: value = (Boolean) getObject(columnName);
0350: } catch (ClassCastException e) {
0351: handleClassCastException(e, value, "Boolean");
0352: }
0353: return value;
0354: }
0355:
0356: /**
0357: * Returns field value as Date
0358: *
0359: * @param columnName
0360: * @return field value as Date
0361: */
0362: public Date getDate(String columnName) {
0363: Date value = null;
0364: try {
0365: value = (Date) getObject(columnName);
0366: } catch (ClassCastException e) {
0367: handleClassCastException(e, value, "Date");
0368: }
0369: return value;
0370: }
0371:
0372: /**
0373: * Returns field value as String
0374: *
0375: * @param columnName
0376: * @return field value as String
0377: */
0378: public String getString(String columnName) {
0379: String value = null;
0380: try {
0381: value = (String) getObject(columnName);
0382: } catch (ClassCastException e) {
0383: handleClassCastException(e, value, "String");
0384: }
0385: return value;
0386: }
0387:
0388: /**
0389: * Returns field value as Object
0390: *
0391: * @param columnName
0392: * @return field value as Object
0393: */
0394: public Object getObject(String columnName) {
0395: return entries[getIndex(columnName)].getValue();
0396: }
0397:
0398: /**
0399: * Sets field value by column name
0400: *
0401: * @param columnName
0402: * @param value
0403: */
0404: public void setObject(String columnName, Object value) {
0405: setObject(getIndex(columnName), value);
0406: }
0407:
0408: /**
0409: * @param columnName
0410: * @return original value as Object
0411: */
0412: public Object getOriginalObject(String columnName) {
0413: return entries[getIndex(columnName)].getOriginalValue();
0414: }
0415:
0416: //
0417: // Getters/Setters by index:
0418: //
0419:
0420: /**
0421: * @param index
0422: * @return true is value is null
0423: */
0424: public boolean isNull(int index) {
0425: return getObject(index) == null;
0426: }
0427:
0428: /**
0429: * @param index
0430: * @return field value as Number
0431: */
0432: public Number getNumber(int index) {
0433: Number value = null;
0434: try {
0435: Object obj = getObject(index);
0436: if (obj != null) {
0437: value = (Number) obj;
0438: }
0439: } catch (ClassCastException e) {
0440: handleClassCastException(e, value, "Number");
0441: }
0442: return value;
0443: }
0444:
0445: /**
0446: * @param index
0447: * @return field value as Boolean
0448: */
0449: public Boolean getBoolean(int index) {
0450: Boolean value = null;
0451: try {
0452: Object obj = getObject(index);
0453: if (obj != null) {
0454: value = (Boolean) obj;
0455: }
0456: } catch (ClassCastException e) {
0457: handleClassCastException(e, value, "Boolean");
0458: }
0459: return value;
0460: }
0461:
0462: /**
0463: * Returns field value as Date
0464: *
0465: * @param index
0466: * @return field value as Date
0467: */
0468: public Date getDate(int index) {
0469: Date value = null;
0470: try {
0471: Object obj = getObject(index);
0472: if (obj != null) {
0473: value = (Date) obj;
0474: }
0475: } catch (ClassCastException e) {
0476: handleClassCastException(e, value, "Date");
0477: }
0478: return value;
0479: }
0480:
0481: /**
0482: * Returns field value as String
0483: *
0484: * @param index
0485: * @return field value as String
0486: */
0487: public String getString(int index) {
0488: String value = null;
0489: try {
0490: Object obj = getObject(index);
0491: if (obj != null) {
0492: value = obj.toString();
0493: }
0494: } catch (ClassCastException e) {
0495: handleClassCastException(e, value, "String");
0496: }
0497: return value;
0498: }
0499:
0500: /**
0501: * Returns field value as Object
0502: *
0503: * @param index
0504: * @return field value as Object
0505: */
0506: public Object getObject(int index) {
0507: validateIndex(index);
0508: return entries[index].getValue();
0509: }
0510:
0511: /**
0512: * Sets field value by index
0513: *
0514: * @param index
0515: * @param value
0516: */
0517: public boolean setObject(int index, Object value) {
0518: if (value == null && notOverwriteWithNulls) {
0519: return false;
0520: }
0521: if (different(getObject(index), value)) {
0522: PersistentEntry entry = entries[index];
0523: entry.setAutoPadChars(autoPadChars);
0524: moveRecordState(entry, value);
0525: return true;
0526: }
0527: return false;
0528: }
0529:
0530: /**
0531: * @param index
0532: * @return original value as Object
0533: */
0534: public Object getOriginalObject(int index) {
0535: validateIndex(index);
0536: return entries[index].getOriginalValue();
0537: }
0538:
0539: /**
0540: * @param entry
0541: * @param value
0542: * @see State
0543: */
0544: private void moveRecordState(PersistentEntry entry, Object value) {
0545: if (state == State.NEW_INITIALIZING) {
0546: if (entry != null) {
0547: entry.setValue(value, value);
0548: return;
0549: }
0550: } else if (state == State.NEW || state == State.NEW_MODIFIED) {
0551: state = State.NEW_MODIFIED;
0552: } else if (state == State.DELETED) {
0553: // Do nothing here
0554: } else {
0555: // gets here if state is DATA_SAVED/DATA_MODIFIED
0556: state = State.DATA_MODIFIED;
0557: }
0558: if (entry != null) {
0559: entry.setValue(value);
0560: }
0561: }
0562:
0563: /**
0564: * Moves record state to the next appropriate position
0565: *
0566: * @see State
0567: */
0568: void moveRecordState() {
0569: moveRecordState(null, null);
0570: }
0571:
0572: /**
0573: * @param index
0574: * @return PersistentEntry
0575: */
0576: public PersistentEntry getEntry(int index) {
0577: validateIndex(index);
0578: return entries[index];
0579: }
0580:
0581: /**
0582: * @param columnName
0583: * @return PersistentEntry
0584: */
0585: public PersistentEntry getEntry(String columnName) {
0586: return entries[getIndex(columnName)];
0587: }
0588:
0589: private void handleClassCastException(Exception e,
0590: Object sourceValue, String targetClass) {
0591: if (e instanceof ClassCastException) {
0592: throw new RuntimeException(": Cannot cast value of class ["
0593: + (sourceValue != null ? sourceValue.getClass()
0594: .getName() : "null") + "] to class ["
0595: + targetClass + "]: " + e.getMessage(), e);
0596: }
0597: }
0598:
0599: /**
0600: * @return number of persistent entries
0601: */
0602: public int size() {
0603: return entries.length;
0604: }
0605:
0606: private void validateIndex(int index) {
0607: if (index < 0 || index > entries.length) {
0608: throw new IndexOutOfBoundsException(
0609: "Cannot find column by index " + index);
0610: }
0611: }
0612:
0613: /**
0614: * @param columnName
0615: * @return column index corresponding to column name
0616: */
0617: public int getIndex(String columnName) {
0618: MetaColumn column = table.getColumn(columnName);
0619: if (column == null) {
0620: throw new IllegalArgumentException(
0621: "Cannot find column by columnName " + columnName);
0622: }
0623: return column.getColumnIndex();
0624: }
0625:
0626: //
0627: // Flags:
0628: //
0629:
0630: /**
0631: * Traverses through of child objects of parent persistent object of this record
0632: * and returns true if at least one of them is dirty. If persistent object is not complex - returns false
0633: *
0634: * @return true if at least one of child objects of parent persistent object of this record
0635: * is dirty
0636: */
0637: public boolean complexDirty() {
0638: if (persistentObject != null && persistentObject.complex()) {
0639: if (dependencyGraph != null) {
0640: if (persistentObject.initializedComplex()) {
0641: return dependencyGraph.dirty();
0642: } else {
0643: throw new OdalRuntimePersistencyException(
0644: "Complex object not initialized");
0645: }
0646: }
0647: } else {
0648: return isDirty();
0649: }
0650: return false;
0651: }
0652:
0653: /**
0654: * Returns true if one of the fields has been modified
0655: *
0656: * @return true if one of the fields is modified
0657: */
0658: public boolean isDirty() {
0659: return state.isDirty();
0660: }
0661:
0662: /**
0663: * @param index
0664: * @return if field is modified
0665: */
0666: public boolean isFieldDirty(int index) {
0667: validateIndex(index);
0668: return entries[index].isDirty();
0669: }
0670:
0671: /**
0672: * @param columnName
0673: * @return if field is modified
0674: */
0675: public boolean isFieldDirty(String columnName) {
0676: return entries[getIndex(columnName)].isDirty();
0677: }
0678:
0679: /**
0680: * @param index
0681: * @param dirty
0682: */
0683: public void setFieldDirty(int index, boolean dirty) {
0684: validateIndex(index);
0685: entries[index].setDirty(dirty);
0686: }
0687:
0688: /**
0689: * @param columnName
0690: * @param dirty
0691: */
0692: public void setFieldDirty(String columnName, boolean dirty) {
0693: entries[getIndex(columnName)].setDirty(dirty);
0694: }
0695:
0696: /**
0697: * Marks field as dirty
0698: *
0699: * @param index
0700: */
0701: public void markFieldDirty(int index) {
0702: setFieldDirty(index, true);
0703: }
0704:
0705: /**
0706: * Resets dirty flags
0707: *
0708: * @param index
0709: */
0710: public void unmarkFieldDirty(int index) {
0711: setFieldDirty(index, false);
0712: }
0713:
0714: /**
0715: * Marks all fields as dirty
0716: */
0717: public void markAllFieldsDirty() {
0718: for (int i = 0; i < size(); i++) {
0719: markFieldDirty(i);
0720: }
0721: }
0722:
0723: /**
0724: * Resets dirty flags
0725: */
0726: public void unmarkAllFieldsDirty() {
0727: for (int i = 0; i < size(); i++) {
0728: unmarkFieldDirty(i);
0729: }
0730: hasDirtyNonKeyFields = false;
0731: }
0732:
0733: /**
0734: * Resets record state and dirty flags
0735: */
0736: public void resetAfterModify() {
0737: for (int i = 0; i < size(); i++) {
0738: if (state == State.NEW_MODIFIED
0739: || state == State.NEW_INITIALIZING) {
0740: getEntry(i).setValue(getEntry(i).getValue(),
0741: getEntry(i).getValue());
0742: }
0743: unmarkFieldDirty(i);
0744: }
0745: hasDirtyNonKeyFields = false;
0746: state = State.DATA_SAVED;
0747: }
0748:
0749: /**
0750: * Returns true if this record came from or been saved to database
0751: *
0752: * @return true if this record came from or been saved to database
0753: */
0754: public boolean isInitialized() {
0755: return state.isInitialized();
0756: }
0757:
0758: /**
0759: * Return true this record has been deleted
0760: *
0761: * @return true this record has been deleted
0762: */
0763: public boolean isDeleted() {
0764: return state.isDeleted();
0765: }
0766:
0767: /**
0768: * Returns true if this record has been saved to database
0769: *
0770: * @return true if this record has been saved to database
0771: */
0772: public boolean isSaved() {
0773: return state.isSaved();
0774: }
0775:
0776: /**
0777: * Returns true if record values will not be overwritten by null values
0778: *
0779: * @return true if record values will not be overwritten by null values
0780: */
0781: public boolean isNotOverwriteWithNulls() {
0782: return notOverwriteWithNulls;
0783: }
0784:
0785: /**
0786: * @param notOverwriteWithNulls true if record values will not be overwritten by null values
0787: */
0788: public void setNotOverwriteWithNulls(boolean notOverwriteWithNulls) {
0789: this .notOverwriteWithNulls = notOverwriteWithNulls;
0790: }
0791:
0792: /**
0793: * Returns true is before and after values are different or
0794: * the record is not yet initialized
0795: *
0796: * @param before
0797: * @param after
0798: * @return true is before and after values are different or the record is not yet initialized
0799: */
0800: protected boolean different(Object before, Object after) {
0801: return different(this , before, after);
0802: }
0803:
0804: public static boolean different(Record record, Object before,
0805: Object after) {
0806: if (record == null) {
0807: throw new IllegalArgumentException("Record is null");
0808: }
0809: if (!record.isInitialized()) {
0810: return true;
0811: }
0812: return PersistentEntry.different(before, after);
0813: }
0814:
0815: /**
0816: * Right pads character fields to column length with spaces.
0817: *
0818: * @param index
0819: * @param value
0820: * @return Object
0821: */
0822: public Object v2c(int index, Object value) {
0823: if (value == null) {
0824: return null;
0825: }
0826: return table.getColumn(index).v2c(value);
0827: }
0828:
0829: /**
0830: * Right pads character fields to column length with spaces.
0831: *
0832: * @param columnName
0833: * @param value
0834: * @return Object
0835: */
0836: public Object v2c(String columnName, Object value) {
0837: if (value == null) {
0838: return null;
0839: }
0840: return table.getColumn(columnName).v2c(value);
0841: }
0842:
0843: /**
0844: * Return true if this record contains no persistent entries
0845: *
0846: * @return true if this record contains no persistent entries
0847: */
0848: public boolean isEmpty() {
0849: return entries.length == 0;
0850: }
0851:
0852: /**
0853: * Returns <tt>Parameters</tt> object based on values and column types of the record for columns with indeces
0854: * specified with "indeces" parameter. This method will populate parameters regardless of whether they are
0855: * "dirty" flags or nulls
0856: *
0857: * @param indeces
0858: * @return Parameters object based on values and column types of the record for columns with indeces
0859: * specified with "indeces" parameter
0860: */
0861: public Parameters toParameters(int[] indeces) {
0862: Parameters parameters = new Parameters(indeces.length);
0863: for (int i = 0; i < indeces.length; i++) {
0864: ColumnType columnType = getTable().getColumn(i).getType();
0865: parameters.add(columnType, getObject(indeces[i]));
0866: }
0867: return parameters;
0868: }
0869:
0870: /**
0871: * Returns <tt>Parameters</tt> object based on values and column types of the record.
0872: * This method will populate parameters only if corresponding fields are
0873: * "dirty" and not nulls
0874: *
0875: * @param primaryKeyOnly if true only primary key columns are considered
0876: * @return Parameters object based on values and column types of the record
0877: */
0878: public Parameters toNotNullParameters(boolean primaryKeyOnly) {
0879: return toNotNullParameters(this , primaryKeyOnly);
0880: }
0881:
0882: /**
0883: * Returns <tt>Parameters</tt> object based on values and column types of the record.
0884: * This method will populate parameters only if corresponding fields are "dirty"
0885: *
0886: * @param primaryKeyOnly if true only primary key columns are considered
0887: * @return Parameters object based on values and column types of the record
0888: */
0889: public Parameters toParameters(boolean primaryKeyOnly) {
0890: return toParameters(this , primaryKeyOnly);
0891: }
0892:
0893: /**
0894: * Returns <tt>Parameters</tt> object based on values and column types of the record .
0895: * This method will populate parameters only if corresponding fields are
0896: * "dirty" and not nulls
0897: *
0898: * @param record <tt>Record</tt>
0899: * @param primaryKeyOnly if true only primary key columns are considered
0900: * @return Parameters object based on values and column types of the record
0901: */
0902: public static Parameters toNotNullParameters(Record record,
0903: boolean primaryKeyOnly) {
0904: return toParameters(record, primaryKeyOnly,
0905: NOT_NULL_TO_PARAMETERS_POLICY);
0906: }
0907:
0908: /**
0909: * Returns <tt>Parameters</tt> object based on values and column types of the record.
0910: * This method will populate all parameters only if corresponding fields are "dirty"
0911: *
0912: * @param record <tt>Record</tt>
0913: * @param primaryKeyOnly if true only primary key columns are considered
0914: * @return Parameters object based on values and column types of the record
0915: */
0916: public static Parameters toParameters(Record record,
0917: boolean primaryKeyOnly) {
0918: return toParameters(record, primaryKeyOnly,
0919: DEFAULT_TO_PARAMETERS_POLICY);
0920: }
0921:
0922: /**
0923: * Returns <tt>Parameters</tt> object based on values and column types of the record.
0924: * This method will populate parameters with accord to <tt>ToParametersPolicy</tt>
0925: *
0926: * @param record <tt>Record</tt>
0927: * @param primaryKeyOnly if true only primary key columns are considered
0928: * @param policy <tt>ToParametersPolicy</tt> object
0929: * @return Parameters object based on values and column types of the record and ctl parameter
0930: * @see ToParametersPolicy
0931: */
0932: public static Parameters toParameters(Record record,
0933: boolean primaryKeyOnly, ToParametersPolicy policy) {
0934: Parameters parameters;
0935: MetaTable table = record.getTable();
0936: if (primaryKeyOnly) {
0937: parameters = new Parameters(table.keySize());
0938: for (int pkIndex = 0; pkIndex < table.keySize(); pkIndex++) {
0939: int columnIndex = table
0940: .getColumnIndexByPrimaryKeyIndex(pkIndex);
0941: Object value = record.getObject(columnIndex);
0942: ColumnType type = table.getColumn(columnIndex)
0943: .getType();
0944: policy.add(columnIndex, type, value, parameters);
0945: }
0946: } else {
0947: parameters = new Parameters(record.size());
0948: for (int i = 0; i < record.size(); i++) {
0949: if (record.isFieldDirty(i)) {
0950: Object value = record.getObject(i);
0951: ColumnType type = table.getColumn(i).getType();
0952: policy.add(i, type, value, parameters);
0953: }
0954: }
0955: }
0956: return parameters;
0957: }
0958:
0959: /**
0960: * Get child link result w/o
0961: * causing lazy retrieval to fire
0962: *
0963: * @param name
0964: * @return Object
0965: */
0966: protected Object getChildObjectStraight(String name) {
0967: Link link = getChild(name);
0968: if (link == null) {
0969: return null;
0970: } else {
0971: return link.getResult();
0972: }
0973: }
0974:
0975: /**
0976: * Gets child link result object
0977: *
0978: * @param name
0979: * @return child link result object
0980: * @throws SQLException if child retrieval from database failed
0981: */
0982: public Object getChildObject(String name) throws SQLException {
0983: Link link = getChild(name);
0984: if (link == null) {
0985: return null;
0986: } else if (link.getResult() != null || link.isRetrieved()) {
0987: return link.getResult();
0988: } else if (link.isLazyRetrieval()) {
0989: link.setRetrieved(true);
0990: QueryCtl query = (QueryCtl) link.getQuery();
0991: Persistency persistency = query.getPersistency();
0992: if (persistency == null) {
0993: return null;
0994: }
0995: Transaction currentTransaction = persistency
0996: .getTransactionManager().getCurrentTransaction();
0997: PersistencyChildManager childManager = ((PersistencyChildManager) persistency);
0998: QueryContext queryContext = childManager
0999: .getCurrentQueryContext();
1000:
1001: // With lazy loading queryContext may be null if the loading is done outside of the query scope:
1002: if (queryContext == null) {
1003: queryContext = QueryContext.NULL_QUERY_CONTEXT;
1004: }
1005:
1006: childManager.selectChild(currentTransaction,
1007: new PersistentObject(this ), name, link,
1008: queryContext);
1009: childManager.selectChildren(currentTransaction, link,
1010: queryContext);
1011: return link.getResult();
1012: } else {
1013: return null;
1014: }
1015: }
1016:
1017: public Link getChild(String name) {
1018: return toLink().getChild(name);
1019: }
1020:
1021: public void clearNonInlinedChildren() {
1022: toLink().clearNonInlinedChildren();
1023: }
1024:
1025: /**
1026: * Create new Record object with attributes base on this record
1027: *
1028: * @return new Record object with attributes base on this record
1029: */
1030: public Record newRecord() {
1031: Record record = newRecord0();
1032: LinkedHashMap newChildren = toLink().newChildren(
1033: record.toLink());
1034: record.toLink().setChildren(newChildren);
1035: return record;
1036: }
1037:
1038: private Record newRecord0() {
1039: Record record = new Record(table);
1040: setupPrimitiveFields(this , record);
1041: onClone(record);
1042: return record;
1043: }
1044:
1045: public static void setupPrimitiveFields(Record recordFrom,
1046: Record recordTo) {
1047: recordTo.tableName = recordFrom.tableName;
1048: recordTo.skipInsertForKeysOnly = recordFrom.skipInsertForKeysOnly;
1049: recordTo.hasDirtyNonKeyFields = recordFrom.hasDirtyNonKeyFields;
1050: recordTo.autoPadChars = recordFrom.autoPadChars;
1051: recordTo.notOverwriteWithNulls = recordFrom.notOverwriteWithNulls;
1052: recordTo.preserveOriginalValues = recordFrom.preserveOriginalValues;
1053: recordTo.forceModify = recordFrom.forceModify;
1054: }
1055:
1056: private void onClone(Record record) {
1057: Link this Link = this .toLink();
1058: Link newLink = record.toLink();
1059: newLink.setDependencyIndex(this Link.getDependencyIndex());
1060: newLink.setPath(this Link.getPath());
1061: }
1062:
1063: /**
1064: * @return deep copy of this record
1065: * @throws CloneNotSupportedException
1066: */
1067: public Object clone() throws CloneNotSupportedException {
1068: Record record = (Record) super .clone();
1069: for (int i = 0; i < this .entries.length; i++) {
1070: record.entries[i] = (PersistentEntry) this .getEntries()[i]
1071: .clone();
1072: record.entries[i].setRecord(record);
1073: }
1074: Link link = new Link();
1075: link.copyAll(toLink());
1076: record.link = link;
1077: record.toLink().setChildren(toLink().cloneChildren());
1078: return record;
1079: }
1080:
1081: //
1082: // Facade for table:
1083: //
1084:
1085: /**
1086: * @param columnKey
1087: * @return MetaColumn
1088: */
1089: public MetaColumn getColumn(String columnKey) {
1090: return table.getColumn(columnKey);
1091: }
1092:
1093: /**
1094: * @param columnIndex
1095: * @return MetaColumn
1096: */
1097: public MetaColumn getColumn(int columnIndex) {
1098: return table.getColumn(columnIndex);
1099: }
1100:
1101: /**
1102: * Returns column name
1103: *
1104: * @param columnKey
1105: * @return column name
1106: */
1107: public String getColumnName(String columnKey) {
1108: return getColumn(columnKey).getColumnName();
1109: }
1110:
1111: /**
1112: * Returns column name
1113: *
1114: * @param columnIndex
1115: * @return column name
1116: */
1117: public String getColumnName(int columnIndex) {
1118: return getColumn(columnIndex).getColumnName();
1119: }
1120:
1121: /**
1122: * Returns fully qualified column name: [table name].[column name]
1123: *
1124: * @param columnKey
1125: * @return full column name
1126: */
1127: public String getFullColumnName(String columnKey) {
1128: return getColumn(columnKey).getFullColumnName();
1129: }
1130:
1131: /**
1132: * Returns column alias
1133: *
1134: * @param columnKey
1135: * @return column alias
1136: */
1137: public String getColumnAlias(String columnKey) {
1138: return getColumn(columnKey).getColumnAlias();
1139: }
1140:
1141: /**
1142: * Returns column alias
1143: *
1144: * @param columnIndex
1145: * @return column alias
1146: */
1147: public String getColumnAlias(int columnIndex) {
1148: return getColumn(columnIndex).getColumnAlias();
1149: }
1150:
1151: /**
1152: * Returns fully qualified column name: [table name].[column name]
1153: *
1154: * @param columnIndex
1155: * @return full column name
1156: */
1157: public String getFullColumnName(int columnIndex) {
1158: return getColumn(columnIndex).getFullColumnName();
1159: }
1160:
1161: /**
1162: * Return List<Integer> of primary key indeces
1163: *
1164: * @return List of primary key indeces
1165: */
1166: public List getPrimaryKey() {
1167: return table.getPrimaryKey();
1168: }
1169:
1170: /**
1171: * Return List<Integer> of optimistic lock column indeces
1172: *
1173: * @return List of optimistic lock column indeces
1174: */
1175: public List getOptLockKey() {
1176: return table.getOptLockKey();
1177: }
1178:
1179: /**
1180: * Return number of record columns
1181: *
1182: * @return number of record columns
1183: */
1184: public int getColumnCount() {
1185: return table.size();
1186: }
1187:
1188: /**
1189: * Implementation of Externalizable interface
1190: *
1191: * @param out
1192: * @throws IOException
1193: * @see java.io.Externalizable
1194: */
1195: public void writeExternal(ObjectOutput out) throws IOException {
1196: out.writeBoolean(isPreserveOriginalValues());
1197: if (isPreserveOriginalValues()) {
1198: out.writeObject(state.getName());
1199: }
1200: for (int i = 0; i < entries.length; i++) {
1201: PersistentEntry entry = entries[i];
1202: writeObject(out, entry.getValue());
1203: if (isPreserveOriginalValues()) {
1204: out.writeBoolean(entry.isDirty());
1205: if (MetaColumn.isKey(entry.getColumn())) {
1206: writeObject(out, entry.getOriginalValue());
1207: }
1208: }
1209: }
1210: marshalChildren(out);
1211: marshalDescendant(out);
1212: }
1213:
1214: protected void writeObject(ObjectOutput out, Object value)
1215: throws IOException {
1216: try {
1217: if (value instanceof Serializable) {
1218: out.writeObject(value);
1219: } else if (value instanceof InputStream) {
1220: InputStream inputStream = ((InputStream) value);
1221: BlobImpl blob = new BlobImpl(inputStream, 0);
1222: out.writeObject(blob);
1223: } else if (value instanceof Blob) {
1224: BlobImpl blob = new BlobImpl(((Blob) value)
1225: .getBinaryStream(), 0);
1226: out.writeObject(blob);
1227: } else if (value instanceof Clob) {
1228: ClobImpl clob = new ClobImpl(((Clob) value)
1229: .getCharacterStream());
1230: out.writeObject(clob);
1231: } else { // It will fail in case value is not null:
1232: out.writeObject(value);
1233: }
1234: } catch (SQLException e) {
1235: String typeString = value.getClass().getName();
1236: throw new IOException("Cannot convert value of type "
1237: + typeString + ": " + e);
1238: }
1239: }
1240:
1241: protected Object readObject(ObjectInput in, ColumnType type)
1242: throws IOException, ClassNotFoundException {
1243: Object value = null;
1244: try {
1245: if (type.getValueClass() == InputStream.class) {
1246: value = in.readObject();
1247: if (value instanceof BlobImpl) {
1248: byte[] data = ((BlobImpl) value).getBytes();
1249: byte[] bytes = data == null ? EMPTY_BYTE_ARRAY
1250: : data;
1251: value = new ByteArrayInputStream(bytes);
1252: } else if (value instanceof ClobImpl) {
1253: ClobImpl clob = ((ClobImpl) value);
1254: String data = clob.getData();
1255: byte[] bytes = data == null ? EMPTY_BYTE_ARRAY
1256: : data.getBytes(clob.getCharsetName());
1257: value = new ByteArrayInputStream(bytes);
1258: }
1259: } else {
1260: value = in.readObject();
1261: }
1262: } catch (SQLException e) {
1263: String typeString = value.getClass().getName();
1264: throw new IOException("Cannot convert value of type "
1265: + typeString + ": " + e);
1266: }
1267: return value;
1268: }
1269:
1270: /**
1271: * Serializes child links
1272: *
1273: * @param out
1274: * @throws IOException
1275: */
1276: protected void marshalChildren(ObjectOutput out) throws IOException {
1277: out.writeBoolean(toLink().hasChildren());
1278: if (toLink().hasChildren()) {
1279: out.writeObject(toLink());
1280: }
1281: }
1282:
1283: /**
1284: * Implementation of Externalizable interface
1285: *
1286: * @param in
1287: * @throws IOException
1288: * @throws ClassNotFoundException
1289: * @see java.io.Externalizable
1290: */
1291: public void readExternal(ObjectInput in) throws IOException,
1292: ClassNotFoundException {
1293: preserveOriginalValues = in.readBoolean();
1294: if (isPreserveOriginalValues()) {
1295: String stateName = (String) in.readObject();
1296: state = State.name2state(stateName);
1297: }
1298: for (int i = 0; i < entries.length; i++) {
1299: PersistentEntry entry = entries[i];
1300: Object value = readObject(in, entry.getType()); // Read value
1301: Object originalValue = value;
1302: if (isPreserveOriginalValues()) {
1303: boolean dirty = in.readBoolean();
1304: if (MetaColumn.isKey(entry.getColumn())) {
1305: originalValue = readObject(in, entry.getType()); // Read original value
1306: }
1307: entry.setUnmarkedValue(originalValue, originalValue);
1308: entry.setValueIfDiff(value);
1309: entry.setDirty(dirty);
1310: } else {
1311: entry.setUnmarkedValue(value, originalValue);
1312: }
1313: }
1314: unmarshalChildren(in);
1315: unmarshalDescendant(in);
1316: }
1317:
1318: /**
1319: * Deserializes child links
1320: *
1321: * @param in
1322: * @throws IOException
1323: * @throws ClassNotFoundException
1324: */
1325: protected void unmarshalChildren(ObjectInput in)
1326: throws IOException, ClassNotFoundException {
1327: boolean hasChildren = in.readBoolean();
1328: if (hasChildren) {
1329: Link link = (Link) in.readObject();
1330: toLink().setChildren(link.getChildren());
1331: LinkedHashMap children = toLink().getChildren();
1332: //
1333: // Now re-initialize children - put elements that had been missed in serialization:
1334: //
1335: PersistentObject persistent = persistentObject;
1336: setupUnmarshalledChildren(persistent, children);
1337: }
1338: }
1339:
1340: public static void setupUnmarshalledChildren(
1341: PersistentObject persistent, LinkedHashMap children) {
1342: if (persistent != null) {
1343: PersistentObject master = persistent.master();
1344: if (master != null) {
1345: Record masterRecord = master.record();
1346: LinkedHashMap masterChildren = masterRecord.toLink()
1347: .getChildren();
1348: for (Iterator it = masterChildren.keySet().iterator(); it
1349: .hasNext();) {
1350: String columnName = (String) it.next();
1351: Link masterLink = (Link) masterChildren
1352: .get(columnName);
1353: Link slaveLink = (Link) children.get(columnName);
1354:
1355: if (slaveLink != null) {
1356: slaveLink.setInlineMode(masterLink
1357: .getInlineMode());
1358: slaveLink.setLifeCycleController(masterLink
1359: .getLifeCycleController());
1360: slaveLink.setQuery(masterLink.getQuery()
1361: .newQuery());
1362: slaveLink.setParentIndeces(masterLink
1363: .getParentIndeces());
1364: slaveLink.setThisIndeces(masterLink
1365: .getThisIndeces());
1366: slaveLink.setDependencyIndex(masterLink
1367: .getDependencyIndex());
1368: slaveLink.setLazyRetrieval(masterLink
1369: .isLazyRetrieval());
1370: slaveLink.setCascadeDelete(masterLink
1371: .isCascadeDelete());
1372: slaveLink.setCascadeInsert(masterLink
1373: .isCascadeInsert());
1374: slaveLink.setCascadeUpdate(masterLink
1375: .isCascadeUpdate());
1376: slaveLink.setTreatNullAsRemove(masterLink
1377: .isTreatNullAsRemove());
1378: slaveLink.setInsertBeforeParent(masterLink
1379: .isInsertBeforeParent());
1380: } else {
1381: System.err
1382: .println(" Could not find slave link by name "
1383: + columnName);
1384: }
1385: }
1386: }
1387: }
1388: }
1389:
1390: // protected Map toMap() {
1391: // Map map = new HashMap();
1392: // ArrayList entryList = new ArrayList(entries.length);
1393: // map.put("entries", entryList);
1394: // for (int i = 0; i < entries.length; i++) {
1395: // PersistentEntry entry = entries[i];
1396: // entryList.add(entry.toMap());
1397: // }
1398: // map.put("children", toMapChildren());
1399: // map.put("descendant", toMapDescendant());
1400: // return map;
1401: // }
1402: //
1403: // protected Map toMapChildren() {
1404: // Map map = new HashMap();
1405: // map.put("hasChildren", Boolean.valueOf(toLink().hasChildren()));
1406: // return map;
1407: // }
1408:
1409: // protected Map toMapDescendant() {
1410: // return null;
1411: // }
1412:
1413: /**
1414: * Method to be implemented in descendants to add to objects being serialized thorugh
1415: * writeExternal(ObjectOutput out) method
1416: *
1417: * @param out
1418: * @throws IOException
1419: */
1420: protected void marshalDescendant(ObjectOutput out)
1421: throws IOException {
1422: }
1423:
1424: /**
1425: * Method to be implemented in descendants to add to objects being serialized thorugh
1426: * readExternal(ObjectOutput out) method
1427: *
1428: * @param in
1429: * @throws IOException
1430: * @throws ClassNotFoundException
1431: */
1432: protected void unmarshalDescendant(ObjectInput in)
1433: throws IOException, ClassNotFoundException {
1434: }
1435:
1436: /**
1437: * Copies only dirty and updateable fields from source record to this record.
1438: * If updateableFields Set is null then all dirty fields will be copied
1439: *
1440: * @param source source record
1441: * @param updateableFields set of updateable column names
1442: */
1443: public void copyDirty(Record source, Set updateableFields) {
1444: copy(source, updateableFields, true);
1445: }
1446:
1447: /**
1448: * Copies updateable fields from source record to this record.
1449: * If updateableFields Set is null then all dirty fields will be copied.
1450: * If dirtyFieldsOnly flag is true then only dirty fields are copied
1451: *
1452: * @param source source record
1453: * @param updateableFields set of updateable column names
1454: * @param dirtyFieldsOnly if true only dirty fields are copied
1455: */
1456: public void copy(Record source, Set updateableFields,
1457: boolean dirtyFieldsOnly) {
1458: copy(source, updateableFields, dirtyFieldsOnly, false);
1459: }
1460:
1461: /**
1462: * Copies updateable fields from source record to this record.
1463: * If updateableFields Set is null then all dirty fields will be copied.
1464: * If dirtyFieldsOnly flag is true then only dirty fields are copied
1465: * If preserveStates is true then source record fields "dirty" states are passed unchanged
1466: * from source record to this record even if the values are the same
1467: *
1468: * @param source source record
1469: * @param updateableFields set of updateable column names
1470: * @param dirtyFieldsOnly if true only dirty fields are copied
1471: * @param preserveStates if true then field "dirty" states are passed unchanged
1472: * from source record to this record even if the values are the same
1473: */
1474: void copy(Record source, Set updateableFields,
1475: boolean dirtyFieldsOnly, boolean preserveStates) {
1476: if (source.size() != this .size()) {
1477: throw new IllegalArgumentException("Size of input ["
1478: + source
1479: + "] is not equal to size of this object [" + this
1480: + "]");
1481: }
1482:
1483: for (int i = 0; i < source.size(); i++) {
1484: if (isUpdateable(getColumn(i).getColumnName(),
1485: updateableFields)
1486: && (source.isFieldDirty(i) || !dirtyFieldsOnly)) {
1487: if (preserveStates) {
1488: getEntry(i).setValue(source.getEntry(i).getValue(),
1489: source.getEntry(i).getOriginalValue());
1490: setFieldDirty(i, source.isFieldDirty(i));
1491: } else {
1492: setObject(i, source.getObject(i));
1493: }
1494: }
1495: }
1496: if (preserveStates) {
1497: setState(source.getState());
1498: }
1499: }
1500:
1501: /**
1502: * Copies updateable fields from source record to this record.
1503: * If updateableFields Set is null then all dirty fields will be copied.
1504: * If dirtyFieldsOnly flag is true then only dirty fields are copied
1505: * Source record fields "dirty" states are passed unchanged
1506: * from source record to this record even if the values are the same
1507: *
1508: * @param source source record
1509: * @param updateableFields set of updateable column names
1510: * @param dirtyFieldsOnly if true only dirty fields are copied
1511: * from source record to this record even if the values are the same
1512: */
1513: void copyPreseveStates(Record source, Set updateableFields,
1514: boolean dirtyFieldsOnly) {
1515: copy(source, updateableFields, dirtyFieldsOnly, true);
1516: }
1517:
1518: /**
1519: * Copies all fields from source record to this record.
1520: * Source record fields "dirty" states are passed unchanged
1521: * from source record to this record even if the values are the same
1522: *
1523: * @param source source record
1524: * from source record to this record even if the values are the same
1525: */
1526: void copyPreseveStates(Record source) {
1527: copy(source, null, false, true);
1528: }
1529:
1530: private boolean isUpdateable(String columnName, Set updateableFields) {
1531: if (updateableFields != null) {
1532: return updateableFields.contains(columnName);
1533: } else {
1534: return true;
1535: }
1536: }
1537:
1538: //
1539: //
1540: //
1541:
1542: /**
1543: * Gets table name. If set it will supercede the underlying MetaTable one when used to build SQL statements.
1544: *
1545: * @return table name
1546: */
1547: public String getTableName() {
1548: return tableName;
1549: }
1550:
1551: /**
1552: * Sets table name which will supercede the underlying MetaTable one when used to build SQL statements.
1553: *
1554: * @param tableName
1555: */
1556: public void setTableName(String tableName) {
1557: if (tableName == null || tableName.length() == 0) {
1558: this .tableName = table.getTableName();
1559: } else {
1560: this .tableName = tableName;
1561: }
1562: }
1563:
1564: public String getTableAlias() {
1565: return tableAlias;
1566: }
1567:
1568: public void setTableAlias(String tableAlias) {
1569: this .tableAlias = tableAlias;
1570: }
1571:
1572: /**
1573: * Returns string representation of underlying MetaTable
1574: *
1575: * @return string representation of underlying MetaTable
1576: */
1577: public String tableToString() {
1578: return table == null ? null : table.toString();
1579: }
1580:
1581: /**
1582: * Compares field values of this record to another one passed as parameter.
1583: *
1584: * @param record
1585: * @return true if field values of this record are equal to one passed as parameter.
1586: */
1587: public boolean fieldsEqual(Record record) {
1588: if (record == null || this .size() != record.size()) {
1589: return false;
1590: }
1591:
1592: for (int i = 0; i < size(); i++) {
1593: if (getObject(i) == null) {
1594: if (record.getObject(i) != null) {
1595: return false;
1596: }
1597: } else if (!getObject(i).equals(record.getObject(i))) {
1598: return false;
1599: }
1600: }
1601: return true;
1602: }
1603:
1604: /**
1605: * Add child link with specific name
1606: *
1607: * @param link
1608: * @throws NullPointerException if link has no name set
1609: * @see Link
1610: */
1611: public void addChild(Link link) {
1612: toLink().addChild(link);
1613: }
1614:
1615: /**
1616: * Returns record <tt>LinkIterator</tt>
1617: *
1618: * @return <tt>LinkIterator</tt>
1619: */
1620: public LinkIterator linkIterator() {
1621: return toLink().linkIterator();
1622: }
1623:
1624: /**
1625: * Returns true if this record has child links
1626: *
1627: * @return true if this record has child links
1628: */
1629: public boolean hasChildren() {
1630: return toLink().hasChildren();
1631: }
1632:
1633: /**
1634: * Sets result value to link with specific name name
1635: *
1636: * @param name name of child link
1637: * @param value
1638: * @see Link#setResult(Object)
1639: * @see Link#getName()
1640: */
1641: public void setChildObject(String name, Object value) {
1642: toLink().setChildObject(name, value);
1643: }
1644:
1645: /**
1646: * Experimental
1647: *
1648: * @return InlineLink[]
1649: */
1650: public Link[] getInlineLinks() {
1651: return toLink().inlineLinks();
1652: }
1653:
1654: public String toString() {
1655: StringBuffer buffer = new StringBuffer("{entries = [");
1656: if (entries != null) {
1657: for (int i = 0; i < size(); i++) {
1658: if (i > 0) {
1659: buffer.append(" ");
1660: }
1661: buffer.append(entries[i]);
1662: }
1663: }
1664: buffer.append("]");
1665: buffer.append(" tableName = ").append(tableName);
1666: buffer.append(" state = ").append(state);
1667: buffer.append(" hasDirtyNonKeyFields = ").append(
1668: hasDirtyNonKeyFields);
1669: buffer.append(" skipInsertForKeysOnly = ").append(
1670: skipInsertForKeysOnly);
1671: buffer.append(" forceModify = ").append(forceModify);
1672: buffer.append(" autoPadChars = ").append(autoPadChars);
1673: buffer.append("}");
1674:
1675: return buffer.toString();
1676: }
1677:
1678: public boolean equalsByValues(Record thatRecord) {
1679: if (super .equals(thatRecord)) {
1680: return true;
1681: }
1682:
1683: if (this .size() == thatRecord.size()) {
1684: for (int i = 0; i < this .size(); i++) {
1685: boolean rc = true;
1686: if ((this .getObject(i) == null && thatRecord
1687: .getObject(i) != null)
1688: || (this .getObject(i) != null && thatRecord
1689: .getObject(i) == null)) {
1690: rc = false;
1691: } else if (this .getObject(i) != null) {
1692: rc = this .getObject(i).equals(
1693: thatRecord.getObject(i));
1694: }
1695: if (!rc) {
1696: return false;
1697: }
1698: }
1699: return true;
1700: } else {
1701: return false;
1702: }
1703: }
1704:
1705: /**
1706: * Returns true if the record has to be modified even if none of the field is dirty
1707: *
1708: * @return true if the record has to be modified even if none of the field is dirty
1709: */
1710: public boolean isForceModify() {
1711: return forceModify;
1712: }
1713:
1714: /**
1715: * Indicates if the record has to be modified even if none of the field is dirty
1716: *
1717: * @param forceModify indicates if the record has to be modified even if none of the field is dirty
1718: */
1719: public void setForceModify(boolean forceModify) {
1720: this .forceModify = forceModify;
1721: }
1722:
1723: public int[] toPrimaryKeyIndeces() {
1724: return this .table.toPrimaryKeyIndeces();
1725: }
1726:
1727: /**
1728: * Policy interface that determines how to translate this record to <tt>Parameters</tt>
1729: *
1730: * @see Parameters
1731: */
1732: public static interface ToParametersPolicy {
1733: /**
1734: * Adds record value to <tt>Parameters</tt>
1735: *
1736: * @param columnIndex
1737: * @param type
1738: * @param value
1739: * @param parameters
1740: * @return <tt>Parameter</tt> added
1741: */
1742: Parameter add(int columnIndex, ColumnType type, Object value,
1743: Parameters parameters);
1744: }
1745:
1746: /**
1747: * Default ToParametersCtl implementation. Adds all the record values to <tt>Parameters</tt>
1748: */
1749: public static class DefaultToParametersPolicy implements
1750: ToParametersPolicy {
1751: /**
1752: * Adds all record values to <tt>Parameters</tt>
1753: *
1754: * @param columnIndex
1755: * @param type
1756: * @param value
1757: * @param parameters
1758: * @return <tt>Parameter</tt> added
1759: */
1760: public Parameter add(int columnIndex, ColumnType type,
1761: Object value, Parameters parameters) {
1762: return parameters.add(type, value);
1763: }
1764: }
1765:
1766: /**
1767: * ToParametersCtl implementation that adds only not null record values to <tt>Parameters</tt>
1768: */
1769: public static class NotNullToParametersPolicy extends
1770: DefaultToParametersPolicy {
1771: /**
1772: * Adds only not null record values to <tt>Parameters</tt>
1773: *
1774: * @param columnIndex
1775: * @param type
1776: * @param value
1777: * @param parameters
1778: * @return <tt>Parameter</tt> added
1779: */
1780: public Parameter add(int columnIndex, ColumnType type,
1781: Object value, Parameters parameters) {
1782: if (value != null) {
1783: return super.add(columnIndex, type, value, parameters);
1784: } else {
1785: return null;
1786: }
1787: }
1788: }
1789:
1790: }
|