001: /**
002: * Objective Database Abstraction Layer (ODAL)
003: * Copyright (c) 2004, The ODAL Development Group
004: * All rights reserved.
005: * For definition of the ODAL Development Group please refer to LICENCE.txt file
006: *
007: * Distributable under LGPL license.
008: * See terms of license at gnu.org.
009: */package com.completex.objective.components.persistency;
010:
011: import java.util.HashMap;
012: import java.util.Map;
013:
014: /**
015: * Container for one Record value.
016: * Normally it would map to a database table column value.
017: *
018: * @author Gennady Krizhevsky
019: */
020: public class PersistentEntry implements Cloneable {
021:
022: private MetaColumn column;
023: private Record record;
024: private Object value;
025: private Object originalValue;
026: private boolean dirty;
027: private boolean autoPadChars;
028: public static final String KEY_VALUE = "value";
029: public static final String KEY_ORIGINAL_VALUE = "originalValue";
030:
031: /**
032: * Factory method
033: *
034: * @param useRecord if true new PersistentEntry will be created with Record inside
035: * @return new PersistentEntry
036: */
037: public PersistentEntry newInstance(boolean useRecord) {
038: return useRecord ? new PersistentEntry(column, record
039: .newRecord()) : new PersistentEntry(column, null);
040: }
041:
042: /**
043: *
044: * @param column parent MetaColumn
045: * @param record parent Record
046: */
047: public PersistentEntry(MetaColumn column, Record record) {
048: this .column = column;
049: this .record = record;
050: }
051:
052: /**
053: * Set parent Record
054: *
055: * @param record
056: */
057: void setRecord(Record record) {
058: this .record = record;
059: }
060:
061: /**
062: *
063: * @return parent Record
064: */
065: public Record getRecord() {
066: return record;
067: }
068:
069: /**
070: *
071: * @return parent MetaColumn
072: */
073: public MetaColumn getColumn() {
074: return column;
075: }
076:
077: /**
078: * Copies value, originalValue and dirty flag from PersistentEntry passed as parameter to
079: * this one
080: *
081: * @param entry
082: * @return itself
083: */
084: PersistentEntry copyValues(PersistentEntry entry) {
085: value = entry.getValue();
086: originalValue = entry.getOriginalValue();
087: dirty = entry.isDirty();
088: return this ;
089: }
090:
091: /**
092: * Set autoPadChars flag meaning that for ColumnType.CHAR types
093: * the value stored in String will be right padded with spaces to the length of
094: * the parent MetaColumn
095: *
096: * @see MetaColumn#v2c(Object)
097: * @param autoPadChars
098: */
099: public void setAutoPadChars(boolean autoPadChars) {
100: this .autoPadChars = autoPadChars;
101: }
102:
103: /**
104: *
105: * @return true ColumnType.CHAR types have to be auto padded to the length of
106: * the parent MetaColumn
107: */
108: public boolean isAutoPadChars() {
109: return autoPadChars;
110: }
111:
112: /**
113: *
114: * @return value stored in this entry
115: */
116: public Object getValue() {
117: return value;
118: }
119:
120: /**
121: * Set value if it is different from the one kept in this.value field
122: *
123: * @param value
124: */
125: public void setValueIfDiff(Object value) {
126: if (different(this .value, value)) {
127: setValue(value);
128: }
129: }
130:
131: /**
132: * Set value
133: *
134: * @param value
135: */
136: public void setValue(Object value) {
137: value(value);
138: setDirty(true);
139: }
140:
141: /**
142: *
143: * @return get value as Number
144: * @throws OdalRuntimePersistencyException if type transformation is not possible
145: */
146: public Number getNumber() {
147: try {
148: return (Number) getValue();
149: } catch (ClassCastException e) {
150: handleClassCastException(e, value, "Number");
151: }
152: return null;
153: }
154:
155: /**
156: *
157: * @return get value as String
158: * @throws OdalRuntimePersistencyException if type transformation is not possible
159: */
160: public String getString() {
161: try {
162: return (String) getValue();
163: } catch (ClassCastException e) {
164: handleClassCastException(e, value, "String");
165: }
166: return null;
167: }
168:
169: private void handleClassCastException(Exception e,
170: Object sourceValue, String targetClass) {
171: if (e instanceof ClassCastException) {
172: throw new OdalRuntimePersistencyException(
173: ": Cannot cast value of class ["
174: + (sourceValue != null ? sourceValue
175: .getClass().getName() : "null")
176: + "] to class [" + targetClass + "]", e);
177: }
178: }
179:
180: /**
181: *
182: * @return map representation of PersistentEntry
183: */
184: protected Map toMap() {
185: HashMap map = new HashMap();
186: map.put(KEY_VALUE, value);
187: map.put(KEY_ORIGINAL_VALUE, originalValue);
188: return map;
189: }
190:
191: /**
192: * Populates PersistentEntry from its map representation
193: *
194: */
195: protected void fromMap(Map map) {
196: value = map.get(KEY_VALUE);
197: originalValue = map.get(KEY_ORIGINAL_VALUE);
198: }
199:
200: /**
201: * Set both current and original values.
202: * This method should be called only if Record state is NEW_INITIALIZING
203: *
204: * @param value
205: * @param originalValue
206: */
207: public void setValue(Object value, Object originalValue) {
208: setUnmarkedValue(value, originalValue);
209: setDirty(true);
210: }
211:
212: /**
213: * Set both current and original values w/o changing its "dirty" state
214: *
215: * @param value
216: * @param originalValue
217: */
218: public void setUnmarkedValue(Object value, Object originalValue) {
219: if (!ColumnType.isBinary(getColumn().getType())) {
220: setOriginalValue(originalValue);
221: } else {
222: setOriginalValue(ColumnType.NULL_BINARY_OBJECT);
223: }
224: value(value);
225: }
226:
227: private void value(Object value) {
228: if (autoPadChars) {
229: this .value = column.v2c(value);
230: } else {
231: this .value = value;
232: }
233: }
234:
235: /**
236: *
237: * @return originalValue
238: */
239: public Object getOriginalValue() {
240: return originalValue;
241: }
242:
243: /**
244: *
245: * @return originalValue as String
246: * @throws OdalRuntimePersistencyException if type transformation is not possible
247: */
248: public String getOriginalString() {
249: try {
250: return (String) getOriginalValue();
251: } catch (ClassCastException e) {
252: handleClassCastException(e, value, "String");
253: }
254: return null;
255: }
256:
257: /**
258: *
259: * @return originalValue as Number
260: * @throws OdalRuntimePersistencyException if type transformation is not possible
261: */
262: public Number getOriginalNumber() {
263: try {
264: return (Number) getOriginalValue();
265: } catch (ClassCastException e) {
266: handleClassCastException(e, value, "Number");
267: }
268: return null;
269: }
270:
271: private void setOriginalValue(Object originalValue) {
272: this .originalValue = originalValue;
273: }
274:
275: /**
276: * Set "dirty" flag
277: *
278: * @param dirty
279: */
280: public void setDirty(boolean dirty) {
281: this .dirty = dirty;
282: if (!column.isPrimaryKey() && record != null) {
283: record.setHasDirtyNonKeyFields(dirty);
284: }
285: if (dirty && record != null) {
286: record.moveRecordState();
287: }
288: }
289:
290: /**
291: *
292: * @return "dirty" flag
293: */
294: public boolean isDirty() {
295: return dirty;
296: }
297:
298: /**
299: *
300: * @param before
301: * @param after
302: * @return true if "before" and "after" objects are different
303: */
304: public static boolean different(Object before, Object after) {
305: if (before == null && after == null) {
306: return false;
307: }
308:
309: if (before != null && !before.equals(after) || after != null
310: && !after.equals(before)) {
311: if (before != null && after != null) {
312: if (before instanceof Number) {
313: return !String.valueOf(before).equals(
314: String.valueOf(after));
315: }
316: }
317: return true;
318: }
319: return false;
320: }
321:
322: public ColumnType getType() {
323: return column == null ? null : column.getType();
324: }
325:
326: /**
327: *
328: * @return copy of this object
329: * @throws CloneNotSupportedException
330: */
331: public Object clone() throws CloneNotSupportedException {
332: return super .clone();
333: }
334:
335: public Object cloneDeep() throws CloneNotSupportedException {
336: PersistentEntry entry = (PersistentEntry) super .clone();
337: entry.column = column.cloneSafe();
338: return entry;
339: }
340:
341: public PersistentEntry cloneSafe() {
342: try {
343: return (PersistentEntry) clone();
344: } catch (CloneNotSupportedException e) {
345: throw new OdalRuntimePersistencyException(
346: "Cannot clone PersistentEntry", e);
347: }
348: }
349:
350: public PersistentEntry cloneDeepSafe() {
351: try {
352: return (PersistentEntry) cloneDeep();
353: } catch (CloneNotSupportedException e) {
354: throw new OdalRuntimePersistencyException(
355: "Cannot clone PersistentEntry", e);
356: }
357: }
358:
359: public String toString() {
360: return new StringBuffer().append("{").append(" column = ")
361: .append(column.getColumnName()).append(" value = ")
362: .append(value).append(" originalValue = ").append(
363: originalValue).append(" dirty = ")
364: .append(dirty).append("}").toString();
365: }
366:
367: }
|