001: package org.apache.ojb.broker.metadata;
002:
003: /* Copyright 2002-2005 The Apache Software Foundation
004: *
005: * Licensed under the Apache License, Version 2.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: import java.io.Serializable;
019: import java.util.Comparator;
020:
021: import org.apache.commons.lang.SerializationUtils;
022: import org.apache.commons.lang.SystemUtils;
023: import org.apache.commons.lang.builder.ToStringBuilder;
024: import org.apache.commons.lang.builder.ToStringStyle;
025: import org.apache.ojb.broker.OJBRuntimeException;
026: import org.apache.ojb.broker.metadata.fieldaccess.AnonymousPersistentField;
027: import org.apache.ojb.broker.accesslayer.conversions.FieldConversion;
028: import org.apache.ojb.broker.accesslayer.conversions.FieldConversionDefaultImpl;
029: import org.apache.ojb.broker.util.ClassHelper;
030:
031: /**
032: * A FieldDescriptor holds the mapping information for a specific member-variable
033: * of a persistent object.
034: * <br>
035: * Note: Be careful when use references of this class or caching instances of this class,
036: * because instances could become invalid (see {@link MetadataManager}).
037: *
038: * @author <a href="mailto:thma@apache.org">Thomas Mahler<a>
039: * @version $Id: FieldDescriptor.java,v 1.36.2.3 2005/12/21 22:26:10 tomdz Exp $
040: */
041: public class FieldDescriptor extends AttributeDescriptorBase implements
042: XmlCapable, Serializable {
043: private static final long serialVersionUID = 7865777758296851949L;
044:
045: public static final String ACCESS_ANONYMOUS = RepositoryElements.TAG_ACCESS_ANONYMOUS;
046: public static final String ACCESS_READONLY = RepositoryElements.TAG_ACCESS_READONLY;
047: public static final String ACCESS_READWRITE = RepositoryElements.TAG_ACCESS_READWRITE;
048:
049: private int m_ColNo;
050: private String m_ColumnName;
051: private String m_ColumnType;
052: private boolean m_IsKeyField = false;
053: private boolean indexed = false;
054: private boolean m_autoIncrement = false;
055: private String m_sequenceName;
056: private JdbcType m_jdbcType;
057:
058: private int length = 0;
059: private int precision = 0;
060: private int scale = 0;
061: private boolean required = false;
062: private boolean scaleSpecified = false;
063: private boolean precisionSpecified = false;
064: private boolean lengthSpecified = false;
065: private FieldConversion fieldConversion = null;
066: // true if field is used for optimistic locking BRJ
067: private boolean m_locking = false;
068: // if locking is true and updateLock is true then
069: // on save lock columns will be updated.
070: // if false then it is the responsibility of the
071: // dbms to update all lock columns eg using triggers
072: private boolean updateLock = true;
073: private String m_access;
074:
075: /**
076: * returns a comparator that allows to sort a Vector of FieldMappingDecriptors
077: * according to their m_Order entries.
078: */
079: public static Comparator getComparator() {
080: return new Comparator() {
081: public int compare(Object o1, Object o2) {
082: FieldDescriptor fmd1 = (FieldDescriptor) o1;
083: FieldDescriptor fmd2 = (FieldDescriptor) o2;
084: if (fmd1.getColNo() < fmd2.getColNo()) {
085: return -1;
086: } else if (fmd1.getColNo() > fmd2.getColNo()) {
087: return 1;
088: } else {
089: return 0;
090: }
091: }
092: };
093: }
094:
095: /**
096: * Constructor declaration
097: *
098: * @param cld The parent {@link ClassDescriptor}
099: * @param id A field id - unique against all other fields in the {@link ClassDescriptor}
100: */
101: public FieldDescriptor(ClassDescriptor cld, int id) {
102: super (cld);
103: m_ColNo = id;
104: }
105:
106: /**
107: *
108: */
109: public String getColumnName() {
110: return m_ColumnName;
111: }
112:
113: /**
114: * Answer the qualified ColumnName<br>
115: * ie: myTab.name
116: *
117: * @return
118: */
119: public String getFullColumnName() // BRJ
120: {
121: return getClassDescriptor().getFullTableName() + "."
122: + getColumnName();
123: }
124:
125: public void setColumnName(String str) {
126: m_ColumnName = str;
127: }
128:
129: public String getColumnType() {
130: return m_ColumnType;
131: }
132:
133: public void setColumnType(String str) {
134: m_ColumnType = str;
135: m_jdbcType = lookupJdbcType();
136: }
137:
138: /**
139: * Returns the corresponding database {@link JdbcType}) of this field,
140: * defined by the JDBC 3.0 specification, e.g. <em>VARCHAR</em>, <em>VARBINARY</em> ...
141: * <p/>
142: * The complement class is {@link FieldType}) which manage the java field
143: * type, e.g. a <em>String</em>, <em>byte[]</em> ...
144: *
145: * Returns the mapped jdbc type of this field (see complement {@link FieldType}), defined by
146: * the JDBC specification.
147: * @return The jdbc database type of this field.
148: */
149: public JdbcType getJdbcType() {
150: // check if jdbcType is assigned
151: if (m_jdbcType == null) {
152: m_jdbcType = lookupJdbcType();
153: }
154: return m_jdbcType;
155: }
156:
157: /**
158: * determines the JDBC type (represented as an int value as specified
159: * by java.sql.Types) of a FIELDDESCRIPTOR.
160: *
161: * @return int the int value representing the Type according to
162: * java.sql.Types.
163: */
164: private JdbcType lookupJdbcType() {
165: JdbcType result = null;
166: String columnType = getColumnType();
167: // if sql type was not set in metadata we use reflection
168: // to determine sql type by reflection
169: if (columnType == null) {
170: try {
171: result = JdbcTypesHelper
172: .getJdbcTypeByReflection(m_PersistentField
173: .getType().getName());
174: } catch (Exception e) {
175: String eol = SystemUtils.LINE_SEPARATOR;
176: throw new OJBRuntimeException(
177: "Can't automatically assign a jdbc field-type for field: "
178: + eol + this .toXML() + eol
179: + "in class: " + eol
180: + getClassDescriptor(), e);
181: }
182: } else {
183: try {
184: result = JdbcTypesHelper.getJdbcTypeByName(columnType);
185: } catch (Exception e) {
186: String eol = SystemUtils.LINE_SEPARATOR;
187: throw new OJBRuntimeException(
188: "Can't assign the specified jdbc field-type '"
189: + columnType + "' for field: " + eol
190: + this .toXML() + eol + "in class: "
191: + eol + getClassDescriptor(), e);
192: }
193: }
194: return result;
195: }
196:
197: /**
198: * Returns a string representation of this class.
199: */
200: public String toString() {
201: ToStringBuilder buf = new ToStringBuilder(this ,
202: ToStringStyle.DEFAULT_STYLE);
203: buf.append("columnName", m_ColumnName);
204: buf.append("columnType", m_ColumnType);
205: buf.append("isPrimaryKey", m_IsKeyField);
206: buf.append("isLocking", m_locking);
207: buf.append("isAutoincrement", m_autoIncrement);
208: buf.append("access", m_access);
209: buf.append("sequenceName", m_sequenceName);
210: buf.append("jdbcType", m_jdbcType);
211: buf.append("super_class_fields ", "=> " + super .toString());
212: buf.append(SystemUtils.LINE_SEPARATOR);
213: return buf.toString();
214: }
215:
216: /**
217: * Gets the fieldConversion.
218: * @return Returns a FieldConversion
219: */
220: public FieldConversion getFieldConversion() {
221: // if no conversion is specified use the default conversion
222: if (fieldConversion == null) {
223: fieldConversion = new FieldConversionDefaultImpl();
224: }
225: return fieldConversion;
226: }
227:
228: /**
229: * Sets the fieldConversion.
230: * @param fieldConversion The fieldConversion to set
231: * @deprecated use setFieldConversionClassName instead
232: */
233: public void setFieldConversion(FieldConversion fieldConversion) {
234: this .fieldConversion = fieldConversion;
235: }
236:
237: /**
238: * Sets the fieldConversion.
239: * @param fieldConversionClassName The fieldConversion to set
240: */
241: public void setFieldConversionClassName(
242: String fieldConversionClassName) {
243: try {
244: this .fieldConversion = (FieldConversion) ClassHelper
245: .newInstance(fieldConversionClassName);
246: } catch (Exception e) {
247: throw new MetadataException(
248: "Could not instantiate FieldConversion class using default constructor",
249: e);
250: }
251: }
252:
253: public boolean isIndexed() {
254: return indexed;
255: }
256:
257: public void setIndexed(boolean indexed) {
258: this .indexed = indexed;
259: }
260:
261: public boolean isAutoIncrement() {
262: return m_autoIncrement;
263: }
264:
265: public void setAutoIncrement(boolean autoIncrement) {
266: m_autoIncrement = autoIncrement;
267: }
268:
269: public String getSequenceName() {
270: return m_sequenceName;
271: }
272:
273: public void setSequenceName(String sequenceName) {
274: m_sequenceName = sequenceName;
275: }
276:
277: public boolean isPrimaryKey() {
278: return m_IsKeyField;
279: }
280:
281: public void setPrimaryKey(boolean b) {
282: m_IsKeyField = b;
283: }
284:
285: public int getColNo() {
286: return m_ColNo;
287: }
288:
289: /**
290: * Gets the locking.
291: * @return Returns a boolean
292: */
293: public boolean isLocking() {
294: return m_locking;
295: }
296:
297: /**
298: * Sets the locking.
299: * @param locking The locking to set
300: */
301: public void setLocking(boolean locking) {
302: m_locking = locking;
303: }
304:
305: /**
306: * Gets the updateLock
307: * updateLock controls whether the lock fields should be
308: * updated by OJB when a row is saved
309: * If false then the dbms needs to update the lock fields.
310: * The default is true
311: * @return Returns a boolean
312: */
313: public boolean isUpdateLock() {
314: return updateLock;
315: }
316:
317: /**
318: * Sets the updateLock
319: * updateLock controls whether the lock fields should be
320: * updated by OJB when a row is saved.
321: * If false then the dbms needs to update the lock fields.
322: * The default is true
323: * @param updateLock The updateLock to set
324: */
325: public void setUpdateLock(boolean updateLock) {
326: this .updateLock = updateLock;
327: }
328:
329: public void setLength(int length) {
330: this .length = length;
331: }
332:
333: public int getLength() {
334: return this .length;
335: }
336:
337: public void setPrecision(int precision) {
338: this .precision = precision;
339: }
340:
341: public int getPrecision() {
342: return this .precision;
343: }
344:
345: public void setScale(int scale) {
346: this .scale = scale;
347: }
348:
349: public int getScale() {
350: return this .scale;
351: }
352:
353: public boolean isRequired() {
354: return required;
355: }
356:
357: public void setRequired(boolean required) {
358: this .required = required;
359: }
360:
361: public boolean isScaleSpecified() {
362: return scaleSpecified;
363: }
364:
365: public void setScaleSpecified(boolean scaleSpecified) {
366: this .scaleSpecified = scaleSpecified;
367: }
368:
369: public boolean isPrecisionSpecified() {
370: return precisionSpecified;
371: }
372:
373: public void setPrecisionSpecified(boolean precisionSpecified) {
374: this .precisionSpecified = precisionSpecified;
375: }
376:
377: public boolean isLengthSpecified() {
378: return lengthSpecified;
379: }
380:
381: public void setLengthSpecified(boolean lengthSpecified) {
382: this .lengthSpecified = lengthSpecified;
383: }
384:
385: public String getAccess() {
386: return m_access;
387: }
388:
389: public void setAccess(String access) {
390: if (access == null) {
391: access = ACCESS_READWRITE;
392: }
393:
394: if (ACCESS_ANONYMOUS.equals(access)
395: || ACCESS_READONLY.equals(access)
396: || ACCESS_READWRITE.equals(access)) {
397: m_access = access;
398: } else {
399: throw new OJBRuntimeException(
400: "Try to set unkown field 'access' value: " + access);
401: }
402: }
403:
404: public boolean isAccessReadOnly() {
405: return ACCESS_READONLY.equals(getAccess());
406: }
407:
408: /**
409: * Returns <em>true</em> if this field is declared as anonymous field.
410: */
411: public boolean isAnonymous() {
412: return AnonymousPersistentField.class
413: .isAssignableFrom(getPersistentField().getClass()) ? true
414: : false;
415: }
416:
417: /*
418: * @see XmlCapable#toXML()
419: */
420: public String toXML() {
421: RepositoryTags tags = RepositoryTags.getInstance();
422: String eol = SystemUtils.LINE_SEPARATOR;
423:
424: //opening tag + attributes
425: StringBuffer result = new StringBuffer(1024);
426: result.append(" ");
427: result.append(tags
428: .getOpeningTagNonClosingById(FIELD_DESCRIPTOR));
429: result.append(" ");
430: result.append(eol);
431:
432: // // id
433: // String id = new Integer(getColNo()).toString();
434: // result += /*" " +*/ tags.getAttribute(ID, id) + eol;
435:
436: // name
437: result.append(" ");
438: result.append(tags.getAttribute(FIELD_NAME, this
439: .getAttributeName()));
440: result.append(eol);
441:
442: // table not yet implemented
443:
444: // column
445: result.append(" ");
446: result.append(tags.getAttribute(COLUMN_NAME, this
447: .getColumnName()));
448: result.append(eol);
449:
450: // jdbc-type
451: result.append(" ");
452: result.append(tags
453: .getAttribute(JDBC_TYPE, this .getColumnType()));
454: result.append(eol);
455:
456: // primarykey
457: if (this .isPrimaryKey()) {
458: result.append(" ");
459: result.append(tags.getAttribute(PRIMARY_KEY, "true"));
460: result.append(eol);
461: }
462:
463: // nullable
464: if (this .isRequired()) {
465: result.append(" ");
466: result.append(tags.getAttribute(NULLABLE, "false"));
467: result.append(eol);
468: }
469:
470: // indexed not yet implemented
471:
472: // autoincrement
473: if (this .isAutoIncrement()) {
474: result.append(" ");
475: result.append(tags.getAttribute(AUTO_INCREMENT, "true"));
476: result.append(eol);
477: }
478:
479: // locking
480: if (this .isLocking()) {
481: result.append(" ");
482: result.append(tags.getAttribute(LOCKING, "true"));
483: result.append(eol);
484: }
485:
486: // updateLock
487: // default is true so only write if false
488: if (!this .isUpdateLock()) {
489: result.append(" ");
490: result.append(tags.getAttribute(UPDATE_LOCK, "false"));
491: result.append(eol);
492: }
493:
494: // default-fetch not yet implemented
495:
496: // conversion
497: if (this .getFieldConversion().getClass() != FieldConversionDefaultImpl.class) {
498: result.append(" ");
499: result.append(tags.getAttribute(FIELD_CONVERSION,
500: getFieldConversion().getClass().getName()));
501: result.append(eol);
502: }
503:
504: // length
505: if (this .isLengthSpecified()) {
506: result.append(" ");
507: result.append(tags.getAttribute(LENGTH, "" + getLength()));
508: result.append(eol);
509: }
510:
511: // precision
512: if (this .isPrecisionSpecified()) {
513: result.append(" ");
514: result.append(tags.getAttribute(PRECISION, ""
515: + getPrecision()));
516: result.append(eol);
517: }
518:
519: // scale
520: if (this .isScaleSpecified()) {
521: result.append(" ");
522: result.append(tags.getAttribute(SCALE, "" + getScale()));
523: result.append(eol);
524: }
525:
526: // access
527: result.append(" ");
528: result.append(tags.getAttribute(ACCESS, this .getAccess()));
529: result.append(eol);
530:
531: result.append(" />");
532: result.append(eol);
533: return result.toString();
534: }
535:
536: public Object clone() {
537: return SerializationUtils.deserialize(SerializationUtils
538: .serialize(this));
539: }
540: }
|