001: /* ====================================================================
002: * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
003: *
004: * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution,
019: * if any, must include the following acknowledgment:
020: * "This product includes software developed by Jcorporate Ltd.
021: * (http://www.jcorporate.com/)."
022: * Alternately, this acknowledgment may appear in the software itself,
023: * if and wherever such third-party acknowledgments normally appear.
024: *
025: * 4. "Jcorporate" and product names such as "Expresso" must
026: * not be used to endorse or promote products derived from this
027: * software without prior written permission. For written permission,
028: * please contact info@jcorporate.com.
029: *
030: * 5. Products derived from this software may not be called "Expresso",
031: * or other Jcorporate product names; nor may "Expresso" or other
032: * Jcorporate product names appear in their name, without prior
033: * written permission of Jcorporate Ltd.
034: *
035: * 6. No product derived from this software may compete in the same
036: * market space, i.e. framework, without prior written permission
037: * of Jcorporate Ltd. For written permission, please contact
038: * partners@jcorporate.com.
039: *
040: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
041: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
042: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
043: * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
044: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
045: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
046: * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
047: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
048: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
049: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
050: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
051: * SUCH DAMAGE.
052: * ====================================================================
053: *
054: * This software consists of voluntary contributions made by many
055: * individuals on behalf of the Jcorporate Ltd. Contributions back
056: * to the project(s) are encouraged when you make modifications.
057: * Please send them to support@jcorporate.com. For more information
058: * on Jcorporate Ltd. and its products, please see
059: * <http://www.jcorporate.com/>.
060: *
061: * Portions of this software are based upon other open source
062: * products and are subject to their respective licenses.
063: */
064: package com.jcorporate.expresso.core.dataobjects;
065:
066: import com.jcorporate.expresso.core.db.DBException;
067: import com.jcorporate.expresso.core.misc.Base64;
068: import com.jcorporate.expresso.core.misc.ConfigJdbc;
069: import com.jcorporate.expresso.core.misc.ConfigManager;
070: import com.jcorporate.expresso.core.misc.ConfigurationException;
071: import com.jcorporate.expresso.core.misc.DateTime;
072: import com.jcorporate.expresso.core.misc.StringUtil;
073: import com.jcorporate.expresso.core.security.CryptoManager;
074: import com.jcorporate.expresso.kernel.exception.ChainedException;
075: import com.jcorporate.expresso.kernel.util.FastStringBuffer;
076: import org.apache.commons.collections.LRUMap;
077: import org.apache.log4j.Logger;
078: import org.apache.oro.text.regex.Pattern;
079: import org.apache.oro.text.regex.PatternMatcher;
080: import org.apache.oro.text.regex.Perl5Matcher;
081:
082: import java.io.ByteArrayInputStream;
083: import java.io.ByteArrayOutputStream;
084: import java.io.IOException;
085: import java.io.InputStream;
086: import java.io.ObjectOutputStream;
087: import java.io.Serializable;
088: import java.math.BigDecimal;
089: import java.text.ParseException;
090: import java.text.SimpleDateFormat;
091: import java.util.Collections;
092: import java.util.Date;
093: import java.util.HashMap;
094: import java.util.Map;
095:
096: /**
097: * Default implementation of the DataField interface. This class provides some
098: * basic conversion capabilities between different Java types as well as provides
099: * all use change logging capabilities.
100: *
101: * @author Michael Rimov
102: * @since Expresso 5.0
103: */
104: public class DefaultDataField implements DataField, Serializable {
105: private static final BigDecimal zeroDecimal = new BigDecimal("0.00");
106: private static final Integer zeroInteger = new Integer(0);
107: private static final Double zeroDouble = new Double(0.00);
108:
109: /**
110: * It takes quite a bit of horsepower to create a SimpleDateFormat object.
111: * By caching the most recent 30 patterns, we can save a lot of CPU time.
112: */
113: static private Map dateConvertFormatMap = new LRUMap(30);
114:
115: /**
116: * Log4j Logger
117: */
118: private static transient Logger log = Logger
119: .getLogger(DefaultDataField.class);
120:
121: /**
122: * Attribute String for if there's an error with the field.
123: */
124: public static final String ATTRIBUTE_ERROR = "error";
125:
126: /**
127: * Attribute String for what message to display if there's an error with
128: * thie field.
129: */
130: public static final String ATTRIBUTE_ERROR_MESSAGE = "error-message";
131:
132: /**
133: * A Pattern Matcher for examining fields vs validation regular expressions
134: * It has been modified for thead local instantiation to reduce
135: * synchronization.
136: */
137: private static transient ThreadLocal patternMatcher = new ThreadLocal() {
138: protected synchronized Object initialValue() {
139: return new Perl5Matcher();
140: }
141: };
142:
143: /**
144: * A link to my metadata.
145: */
146: protected transient DataFieldMetaData myMetaData;
147:
148: /**
149: * A link to the DataObject that contains me.
150: */
151: protected transient DataObject owner;
152:
153: /**
154: * A generic map of attribute keys to attribute values
155: */
156: protected Map attributes = null;
157:
158: /**
159: * The Current Value
160: */
161: protected Object currentValue = null;
162:
163: /**
164: * The original value, used in comparison tests for 'isChanged' calculation; set by first retrieve(); reset after add() or update()
165: */
166: protected Object originalValue = null;
167:
168: /**
169: * Flag to signify if the field is changed
170: */
171: protected boolean isChanged = false;
172:
173: /**
174: * Flag to signify if the field value has been set - the retrieved value may be null
175: */
176: protected boolean isValueSet = false;
177:
178: /**
179: * Creates a new DefaultDataField object.
180: *
181: * @param metaData
182: * @param parentObject
183: */
184: protected DefaultDataField(DataFieldMetaData metaData,
185: DataObject parentObject) {
186: myMetaData = metaData;
187: owner = parentObject;
188: }
189:
190: /**
191: * create new object for this field
192: *
193: * @param metaData
194: * @param parentObject
195: * @return new instance of this field
196: */
197: public static DefaultDataField getInstance(
198: DataFieldMetaData metaData, DataObject parentObject) {
199: return new DefaultDataField(metaData, parentObject);
200: }
201:
202: /**
203: * set all internal data members to null;
204: */
205: public void release() {
206: currentValue = null;
207: originalValue = null;
208: attributes = null;
209: myMetaData = null;
210: owner = null;
211: isChanged = false;
212: }
213:
214: /**
215: * Used for setting raw data from a JDBC connection. Allows for special
216: * processing such as decryption.
217: *
218: * @param o the object to set.
219: */
220: public void setSerializedForm(Object o) {
221: currentValue = o;
222: originalValue = null;
223: }
224:
225: /**
226: * Returns the form in a way suitable for storage in a database or
227: * other data source.
228: *
229: * @return java.lang.Object
230: * @throws DataException upon error
231: */
232: public Object getSerializedForm() throws DataException {
233: try {
234: if (currentValue == null) {
235: return null;
236: }
237:
238: if (myMetaData.isEncrypted()) {
239: if (!(currentValue instanceof String)) {
240: throw new IllegalArgumentException("Field "
241: + myMetaData.getName()
242: + " must be a string value to be encrypted");
243: }
244: // return Base64.encodeNoPadding(cryptoManager.getStringEncryption()
245: // .encryptString((String)currentValue));
246: return currentValue;
247: } else {
248:
249: String result = currentValue.toString();
250:
251: if (myMetaData.isBooleanType()) {
252: // do we have to convert from true/false to 'Y'/'N' ??
253: try {
254: boolean nativeBoolean = ConfigManager
255: .getContext(owner.getDataContext())
256: .getJdbc().isNativeBool();
257: if (!nativeBoolean) {
258: // fix up a true/false into what we expect to serialize
259: if ("true".equalsIgnoreCase(result)) {
260: result = "Y";
261: }
262: if ("false".equalsIgnoreCase(result)) {
263: result = "N";
264: }
265: }
266: } catch (ConfigurationException ce) {
267: throw new DataException(ce);
268: }
269: }
270: return result;
271: }
272: } catch (ChainedException e) {
273: throw new DataException(e);
274: }
275: }
276:
277: /**
278: * retrieve value object for this field
279: *
280: * @return Object which contains value
281: */
282: public Object getValue() {
283: if ((currentValue != null) && currentValue instanceof String
284: && myMetaData.isEncrypted()) {
285: try {
286: return CryptoManager
287: .getInstance()
288: .getStringEncryption()
289: .decryptString(
290: (Base64
291: .decodeNoPadding((String) currentValue)));
292: } catch (ChainedException ce) {
293: return currentValue;
294: }
295: }
296:
297: return currentValue;
298: }
299:
300: /**
301: * Retrieves the wrapped object as a String
302: *
303: * @return a String value representing the object as a string
304: */
305: public String asString() {
306: Object o = getValue();
307: if (o == null) {
308: return null;
309: }
310:
311: if (o instanceof String) {
312: return (String) o;
313: }
314:
315: if (o instanceof Boolean) {
316: return getBooleanFieldValue((Boolean) o);
317: }
318:
319: if (o instanceof Date) {
320: try {
321: return DateTime.getDateTimeForDB((Date) o, this
322: .getOwner().getDataContext());
323: } catch (DBException ex) {
324: }
325: }
326:
327: return o.toString();
328: }
329:
330: /**
331: * Retrieve the field as a InputStream. If the object's native type is
332: * an input stream, it returns that. If it is a serializable object,
333: * it returns a ByteArrayInputStream that was created using a
334: * ByteArrayOutputStream as well as a ObjectOutputStream.
335: *
336: * @return java.io.InputSTream or null if there was an IO Exception getting
337: * the value, the value was null, of if the native object is not serializable
338: */
339: public InputStream asStream() {
340: Object o = getValue();
341: if (o == null) {
342: return null;
343: }
344:
345: if (o instanceof InputStream) {
346: return (InputStream) o;
347: }
348:
349: if (!(o instanceof Serializable)) {
350: log
351: .error("Object: "
352: + o.getClass().getName()
353: + " is not serializable. Cannot retrieve as stream");
354: return null;
355: }
356:
357: try {
358: ByteArrayOutputStream bos = new ByteArrayOutputStream();
359: ObjectOutputStream oos = new ObjectOutputStream(bos);
360: oos.writeObject(o);
361: oos.flush();
362: oos.close();
363: bos.flush();
364: ByteArrayInputStream bis = new ByteArrayInputStream(bos
365: .toByteArray());
366: bos.close();
367: return bis;
368: } catch (IOException ex) {
369: log.error("Error writing object ot memory stream", ex);
370: return null;
371: }
372: }
373:
374: /**
375: * Retrieves the wrapped object as an <code>Integer</code> or zero if it's an invalid format.
376: *
377: * @return a java.lang.Integer object, or null if the object is null.
378: */
379: public Integer asInteger() {
380: Object o = getValue();
381: if (o == null) {
382: return null;
383: }
384: if (o instanceof Integer) {
385: return (Integer) o;
386: }
387:
388: if (o instanceof String) {
389: try {
390: return new Integer((String) o);
391: } catch (Exception ex) {
392: return zeroInteger;
393: }
394: }
395:
396: return zeroInteger;
397: }
398:
399: /**
400: * Retrieve the wrapped object as a <code>Date</code> object or possibly null if we
401: * can't convert it or the object is null.
402: *
403: * @return a properly instantiated <code>Date</code> Object if we're able to convert it.
404: */
405: public Date asDate() {
406: Object o = getValue();
407: if (o == null) {
408: return null;
409: }
410:
411: if (o instanceof Date) {
412: return (Date) o;
413: }
414:
415: if (o instanceof String) {
416: java.util.Date returnDate = null;
417: String convertFormat = null;
418: ConfigJdbc myConfig = null;
419: String strVal = (String) o;
420:
421: try {
422: myConfig = ConfigManager.getJdbcRequired(this
423: .getOwner().getDataContext());
424: } catch (ConfigurationException ce) {
425: log.error("Error getting data context: "
426: + getOwner().getDataContext());
427: return null;
428: }
429:
430: if (this .getFieldMetaData().isDateOnlyType()) {
431: if (!StringUtil.notNull(myConfig.getDateSelectFormat())
432: .equals("")) {
433: convertFormat = myConfig.getDateSelectFormat();
434: } else {
435: convertFormat = "yyyy-MM-dd";
436: }
437: } else if (this .getFieldMetaData().isDateTimeType()) {
438: if (!StringUtil.notNull(
439: myConfig.getDateTimeSelectFormat()).equals("")) {
440: convertFormat = myConfig.getDateTimeSelectFormat();
441: } else {
442: convertFormat = "yyyy-MM-dd HH:mm:ss";
443: }
444: } else if (this .getFieldMetaData().isTimeType()) {
445: if (!StringUtil.notNull(myConfig.getTimeSelectFormat())
446: .equals("")) {
447: convertFormat = myConfig.getTimeSelectFormat();
448: } else {
449: convertFormat = "HH:mm:ss";
450: }
451: } else {
452: log.warn("Unable to find convert type for class "
453: + o.getClass().getName());
454: return null;
455: }
456:
457: try {
458: //We have to lock the whole map because we don't want somebody
459: //else to call parse while we're working on the value.
460: synchronized (dateConvertFormatMap) {
461: SimpleDateFormat formatter = getSimpleDateFormat(convertFormat);
462: returnDate = formatter.parse(strVal);
463: }
464: } catch (ParseException pe) {
465: log.warn("Error parsing string to date", pe);
466: return null;
467: }
468: if (returnDate != null) {
469: return returnDate;
470: }
471:
472: }
473:
474: log.warn("Unable to find conversion for object "
475: + o.getClass().getName() + " to Date");
476: return null;
477: }
478:
479: /**
480: * Get a SimpleDateFormat object that is cached. Make sure you have
481: * dateConvertFormatMap already locked before calling this function or you'll
482: * have a race condition.
483: *
484: * @param pattern the format pattern to look up
485: * @return an instantiated SimpleDateFormat object. SimpleDateFormat is NOT
486: * threadsafe, so make sure you do your parsing while still in the synchronized
487: * block. Perhaps in the future a keyed Object pool will be better.
488: */
489: protected SimpleDateFormat getSimpleDateFormat(String pattern) {
490: SimpleDateFormat aFormat = null;
491: aFormat = (SimpleDateFormat) dateConvertFormatMap.get(pattern);
492:
493: if (aFormat == null) {
494: aFormat = new SimpleDateFormat(pattern);
495: dateConvertFormatMap.put(pattern, aFormat);
496: }
497:
498: return aFormat;
499: }
500:
501: /**
502: * Retrieve the wrapped object as a <code>BigDecimal</code> object or zero if we're unable
503: * to convert it.
504: *
505: * @return a properly instantiated <code>BigDecimal</code> Object if we're able to convert it.
506: */
507: public BigDecimal asBigDecimal() {
508: Object o = getValue();
509: if (o == null) {
510: return null;
511: }
512:
513: if (o instanceof BigDecimal) {
514: return (BigDecimal) o;
515: }
516:
517: if (o instanceof String) {
518: try {
519:
520: BigDecimal returnValue = new BigDecimal((String) o);
521:
522: return returnValue.setScale(this .getFieldMetaData()
523: .getPrecision());
524: } catch (NumberFormatException ex) {
525: return zeroDecimal.setScale(this .getFieldMetaData()
526: .getPrecision());
527: }
528: }
529:
530: return zeroDecimal;
531: }
532:
533: /**
534: * Retrieve the wrapped object as a <code>Double</code> object or zero if we
535: * can't convert i
536: *
537: * @return a properly instantiated <code>Double</code> Object if we're able to convert it.
538: */
539: public Double asDouble() {
540: Object o = getValue();
541: if (o == null) {
542: return null;
543: }
544:
545: if (o instanceof Double) {
546: return (Double) o;
547: }
548:
549: try {
550: return new Double(o.toString());
551: } catch (NumberFormatException ex) {
552: return zeroDouble;
553: }
554:
555: }
556:
557: /**
558: * Retrieve the boolean object value.
559: *
560: * @return Boolean object. Null if the object's value is null
561: */
562: public Boolean asBoolean() {
563: Object o = getValue();
564: if (o == null) {
565: return null;
566: }
567:
568: if (o instanceof Boolean) {
569: return (Boolean) o;
570: }
571:
572: if (o instanceof String) {
573: String s = (String) o;
574: if ("y".equalsIgnoreCase(s)) {
575: return Boolean.TRUE;
576: } else if ("true".equalsIgnoreCase(s)) {
577: return Boolean.TRUE;
578: }
579: }
580:
581: return Boolean.FALSE;
582: }
583:
584: /**
585: * Retrieve the last original value if the data has changed. Will return
586: * null if isChanged() is false
587: *
588: * @return the original object or null
589: */
590: public Object getOriginalValue() {
591: return originalValue;
592: }
593:
594: /**
595: * Used for change logging and updating only changed fields.
596: * change is true IF the field has been set once (after object is born or reset), and
597: * subsequently the value is changed in the field.
598: *
599: * @return true if the field has changed since the last reset (i.e. since object was constructed or reset)
600: */
601: public boolean isChanged() {
602: return isChanged;
603: // return (originalValue != null);
604: }
605:
606: /**
607: * Used for change logging.
608: *
609: * @return true if the field has had the value set since the last reset
610: */
611: public boolean isValueSet() {
612: return isValueSet;
613: }
614:
615: /**
616: * Make sure the value of the field is valid. Checks mask and for null.
617: *
618: * @throws DataException if the field value is not valid
619: */
620: public void checkValue() throws DataException {
621: DataObjectMetaData ownerMetaData = owner.getMetaData();
622:
623: String fieldDescription;
624: try {
625: fieldDescription = ownerMetaData.getDescription(myMetaData
626: .getName());
627: } catch (DBException dbe) {
628: throw new DataException(dbe);
629: }
630:
631: //Allow autoinc fields through since they're added last minute.
632: if (myMetaData.isAutoIncremented()) {
633: return;
634: }
635:
636: // check for null when not allowed
637: if ((!myMetaData.allowsNull()) && isNull()) {
638: myMetaData.setAttribute(ATTRIBUTE_ERROR, "Y");
639: String msg = (String) myMetaData
640: .getAttribute(ATTRIBUTE_ERROR_MESSAGE);
641: if (msg == null) {
642: FastStringBuffer fsb = FastStringBuffer.getInstance();
643: try {
644: fsb.append("Field '");
645: // TODO not i18n
646: fsb.append(fieldDescription);
647: fsb.append("' cannot accept a null value ");
648: msg = fsb.toString();
649: } finally {
650: fsb.release();
651: fsb = null;
652: }
653: }
654:
655: myMetaData.setAttribute(ATTRIBUTE_ERROR_MESSAGE, msg);
656: throw new DataException(msg);
657: }
658:
659: /**
660: * Modification done for avoid empty content check for read only
661: * field in Add state when you use DBMaint or sevlet like DBMaint to fill table
662: * The field is checked before it's filled in add method within it's own DBObject.
663: * @author Yves Henri AMAIZO
664: */
665: /* Check for null and not read only conlumn*/
666: if ((!myMetaData.allowsNull() && !myMetaData.isReadOnly())
667: && asString().length() == 0) {
668: myMetaData.setAttribute(ATTRIBUTE_ERROR, "Y");
669: String msg = (String) myMetaData
670: .getAttribute(ATTRIBUTE_ERROR_MESSAGE);
671: if (msg == null) {
672: FastStringBuffer fsb = FastStringBuffer.getInstance();
673: try {
674: fsb.append("Field '");
675: // TODO not i18n
676: fsb.append(fieldDescription);
677: fsb.append("' cannot be empty ");
678: msg = fsb.toString();
679: } finally {
680: fsb.release();
681: fsb = null;
682: }
683: }
684:
685: myMetaData.setAttribute(ATTRIBUTE_ERROR_MESSAGE, msg);
686: throw new DataException(msg);
687: }
688:
689: // check mask
690: if (!isNull() && asString().length() > 0) {
691: Pattern mask = myMetaData.getMask();
692:
693: if (mask != null) {
694: if (!getPatternMatcher().matches(asString(), mask)) {
695: myMetaData.setAttribute(ATTRIBUTE_ERROR, "Y");
696: String msg = (String) myMetaData
697: .getAttribute(ATTRIBUTE_ERROR_MESSAGE);
698: if (msg == null) {
699: FastStringBuffer fsb = FastStringBuffer
700: .getInstance();
701: try {
702: fsb.append("Value '");
703: fsb.append(asString());
704: fsb.append("' supplied for field '");
705: // TODO not i18n
706: fsb.append(fieldDescription);
707: fsb
708: .append("' does not match the regular expression specified for this field.");
709: msg = fsb.toString();
710: } finally {
711: fsb.release();
712: fsb = null;
713: }
714:
715: }
716: myMetaData.setAttribute(ATTRIBUTE_ERROR_MESSAGE,
717: msg);
718: msg = msg + " " + asString();
719: throw new DataException(msg);
720: }
721: }
722: }
723: }
724:
725: /**
726: * Resets the isChanged and isValueSet flags and sets the original value field to null
727: */
728: public void resetChanged() {
729: originalValue = null;
730: isChanged = false;
731: isValueSet = false;
732: }
733:
734: /**
735: * Sets the wrapped object for the data field; also updates values for isValueSet and isChanged flags
736: *
737: * @param newValue a new Object to set the value to
738: */
739: public void setValue(Object newValue) {
740: if (originalValue == null) {
741: // we preserve the very first, original value, for use as 'baseline' in computing 'isChanged'
742: originalValue = currentValue;
743: }
744:
745: // consider the case for the first time
746: // that a field is populated by retrieval from the DB.
747: // Retrieval will call setValue here, and this setValue is NOT *changing*
748: // an *existing* value. we keep a boolean isValueSet to determine if
749: // this is the first setting (the retrieval) of this fielddata
750: if (isValueSet) {
751: // are we changing the value?
752: if (newValue == null) {
753: // no change if both are null
754: isChanged = (currentValue != null);
755: } else {
756: // ok, we just use a comparison now that we know one of them is non-null
757: isChanged = !newValue.equals(originalValue);
758: }
759: }
760:
761: currentValue = newValue;
762: isValueSet = true;
763:
764: if (getOwner().getStatus().equals(DataObject.STATUS_CURRENT)) {
765: this .getOwner().setStatus(DataObject.STATUS_UPDATED);
766: }
767: }
768:
769: /**
770: * Returns true if the object is null.
771: *
772: * @return true if the object is null
773: */
774: public boolean isNull() {
775: return (currentValue == null);
776: }
777:
778: /**
779: * Sets an attribute for this particular instance of the Data field
780: *
781: * @param attributeName the name of the attribute to set
782: * @param value the value to set it to
783: */
784: public void setAttribute(String attributeName, Object value) {
785: if (attributes == null) {
786: attributes = new HashMap();
787: }
788: attributes.put(attributeName, value);
789: }
790:
791: /**
792: * Removes an attribute
793: *
794: * @param attribute The attribute key to remove
795: */
796: public void removeAttribute(String attribute) {
797: if (attributes == null) {
798: return;
799: }
800:
801: attributes.remove(attribute);
802: }
803:
804: /**
805: * Retrieves any user defined attributes for this field.
806: *
807: * @param attributeName the name of the attribute to retrieve
808: * @return an object or null if the attribute doesn't exist
809: */
810: public Object getAttribute(String attributeName) {
811: if (attributes == null) {
812: return null;
813: }
814:
815: return attributes.get(attributeName);
816: }
817:
818: /**
819: * Returns a Read Only <code>Map</code> of all attributes in name-value pairs. If there are no
820: * attributes then getAllAttributes will return a null map.
821: *
822: * @return java.util.map
823: */
824: public Map getAllAttributes() {
825: if (attributes == null) {
826: return new HashMap();
827: } else {
828: return Collections.unmodifiableMap(attributes);
829: }
830: }
831:
832: /**
833: * Returns a handle to the DataObject that is the container for this
834: * Data Field
835: *
836: * @return DataObject the containing data object.
837: */
838: public DataObject getOwner() {
839: return this .owner;
840: }
841:
842: /**
843: * Sets the owner of a given DataObject
844: *
845: * @param newOwner The new parent object.
846: */
847: public void setOwner(DataObject newOwner) {
848: this .owner = newOwner;
849: }
850:
851: /**
852: * Returns a handle to the Field MetaData object.
853: *
854: * @return DataFieldMetaData
855: */
856: public DataFieldMetaData getFieldMetaData() {
857: return this .myMetaData;
858: }
859:
860: /**
861: * Sets the metadata object for this field.
862: *
863: * @param newMetadata the new Field Meta data object... for example <code>DBField</code>
864: */
865: public void setFieldMetaData(DataFieldMetaData newMetadata) {
866: this .myMetaData = newMetadata;
867: }
868:
869: /**
870: * Internal refactoring for getting what a boolean field should be set to.
871: *
872: * @param theFieldValue The target value
873: * @return the string value
874: * @throws IllegalArgumentException If we can't get the data context
875: */
876: protected String getBooleanFieldValue(Boolean theFieldValue) {
877: try {
878: boolean fieldValue = theFieldValue.booleanValue();
879: boolean nativeBoolean = ConfigManager.getContext(
880: this .getOwner().getDataContext()).getJdbc()
881: .isNativeBool();
882:
883: if (fieldValue == true) {
884: if (nativeBoolean) {
885: return "true";
886: } else {
887: return "Y";
888: }
889: } else {
890: if (nativeBoolean) {
891: return "false";
892: } else {
893: return "N";
894: }
895: }
896: } catch (ConfigurationException ce) {
897: log.error("Error getting data context.", ce);
898: throw new IllegalArgumentException(
899: "Error getting datacontext "
900: + this .getOwner().getDataContext());
901: }
902: }
903:
904: /**
905: * Retrieve a thread local instance of the Perl5 pattern matcher. Allows
906: * for optimization of # of instances of pattern matcher vs
907: * synchronization.
908: *
909: * @return PatternMatcher
910: */
911: protected PatternMatcher getPatternMatcher() {
912: return (PatternMatcher) patternMatcher.get();
913: }
914:
915: /**
916: * reset 'original' value and isChanged flags;
917: * call when add(), retrieve() or update() has occurred, and currentValue of data fields should be considered 'original value' for purposes of determining 'isChanged'
918: */
919: public void cacheIsChangedComparison() {
920: originalValue = currentValue;
921: isChanged = false;
922: }
923:
924: }
|