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.1 2007-08-24 22:17:29 ewestfal Exp $
040 */
041 public class FieldDescriptor extends AttributeDescriptorBase implements XmlCapable, Serializable
042 {
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 {
081 return new Comparator()
082 {
083 public int compare(Object o1, Object o2)
084 {
085 FieldDescriptor fmd1 = (FieldDescriptor) o1;
086 FieldDescriptor fmd2 = (FieldDescriptor) o2;
087 if (fmd1.getColNo() < fmd2.getColNo())
088 {
089 return -1;
090 }
091 else if (fmd1.getColNo() > fmd2.getColNo())
092 {
093 return 1;
094 }
095 else
096 {
097 return 0;
098 }
099 }
100 };
101 }
102
103 /**
104 * Constructor declaration
105 *
106 * @param cld The parent {@link ClassDescriptor}
107 * @param id A field id - unique against all other fields in the {@link ClassDescriptor}
108 */
109 public FieldDescriptor(ClassDescriptor cld, int id)
110 {
111 super(cld);
112 m_ColNo = id;
113 }
114
115 /**
116 *
117 */
118 public String getColumnName()
119 {
120 return m_ColumnName;
121 }
122
123 /**
124 * Answer the qualified ColumnName<br>
125 * ie: myTab.name
126 *
127 * @return
128 */
129 public String getFullColumnName() // BRJ
130 {
131 return getClassDescriptor().getFullTableName() + "." + getColumnName();
132 }
133
134 public void setColumnName(String str)
135 {
136 m_ColumnName = str;
137 }
138
139 public String getColumnType()
140 {
141 return m_ColumnType;
142 }
143
144 public void setColumnType(String str)
145 {
146 m_ColumnType = str;
147 m_jdbcType = lookupJdbcType();
148 }
149
150 /**
151 * Returns the corresponding database {@link JdbcType}) of this field,
152 * defined by the JDBC 3.0 specification, e.g. <em>VARCHAR</em>, <em>VARBINARY</em> ...
153 * <p/>
154 * The complement class is {@link FieldType}) which manage the java field
155 * type, e.g. a <em>String</em>, <em>byte[]</em> ...
156 *
157 * Returns the mapped jdbc type of this field (see complement {@link FieldType}), defined by
158 * the JDBC specification.
159 * @return The jdbc database type of this field.
160 */
161 public JdbcType getJdbcType()
162 {
163 // check if jdbcType is assigned
164 if(m_jdbcType == null)
165 {
166 m_jdbcType = lookupJdbcType();
167 }
168 return m_jdbcType;
169 }
170
171 /**
172 * determines the JDBC type (represented as an int value as specified
173 * by java.sql.Types) of a FIELDDESCRIPTOR.
174 *
175 * @return int the int value representing the Type according to
176 * java.sql.Types.
177 */
178 private JdbcType lookupJdbcType()
179 {
180 JdbcType result = null;
181 String columnType = getColumnType();
182 // if sql type was not set in metadata we use reflection
183 // to determine sql type by reflection
184 if (columnType == null)
185 {
186 try
187 {
188 result = JdbcTypesHelper.getJdbcTypeByReflection(m_PersistentField.getType().getName());
189 }
190 catch(Exception e)
191 {
192 String eol = SystemUtils.LINE_SEPARATOR;
193 throw new OJBRuntimeException("Can't automatically assign a jdbc field-type for field: "
194 + eol + this.toXML() + eol + "in class: " + eol + getClassDescriptor(), e);
195 }
196 }
197 else
198 {
199 try
200 {
201 result = JdbcTypesHelper.getJdbcTypeByName(columnType);
202 }
203 catch(Exception e)
204 {
205 String eol = SystemUtils.LINE_SEPARATOR;
206 throw new OJBRuntimeException("Can't assign the specified jdbc field-type '"+columnType+"' for field: "
207 + eol + this.toXML() + eol + "in class: " + eol + getClassDescriptor(), e);
208 }
209 }
210 return result;
211 }
212
213 /**
214 * Returns a string representation of this class.
215 */
216 public String toString()
217 {
218 ToStringBuilder buf = new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE);
219 buf.append("columnName", m_ColumnName);
220 buf.append("columnType", m_ColumnType);
221 buf.append("isPrimaryKey", m_IsKeyField);
222 buf.append("isLocking", m_locking);
223 buf.append("isAutoincrement", m_autoIncrement);
224 buf.append("access", m_access);
225 buf.append("sequenceName", m_sequenceName);
226 buf.append("jdbcType", m_jdbcType);
227 buf.append("super_class_fields ", "=> " + super.toString());
228 buf.append(SystemUtils.LINE_SEPARATOR);
229 return buf.toString();
230 }
231
232 /**
233 * Gets the fieldConversion.
234 * @return Returns a FieldConversion
235 */
236 public FieldConversion getFieldConversion()
237 {
238 // if no conversion is specified use the default conversion
239 if (fieldConversion == null)
240 {
241 fieldConversion = new FieldConversionDefaultImpl();
242 }
243 return fieldConversion;
244 }
245
246 /**
247 * Sets the fieldConversion.
248 * @param fieldConversion The fieldConversion to set
249 * @deprecated use setFieldConversionClassName instead
250 */
251 public void setFieldConversion(FieldConversion fieldConversion)
252 {
253 this.fieldConversion = fieldConversion;
254 }
255
256 /**
257 * Sets the fieldConversion.
258 * @param fieldConversionClassName The fieldConversion to set
259 */
260 public void setFieldConversionClassName(String fieldConversionClassName)
261 {
262 try
263 {
264 this.fieldConversion = (FieldConversion) ClassHelper.newInstance(fieldConversionClassName);
265 }
266 catch (Exception e)
267 {
268 throw new MetadataException(
269 "Could not instantiate FieldConversion class using default constructor", e);
270 }
271 }
272
273 public boolean isIndexed()
274 {
275 return indexed;
276 }
277
278 public void setIndexed(boolean indexed)
279 {
280 this.indexed = indexed;
281 }
282
283 public boolean isAutoIncrement()
284 {
285 return m_autoIncrement;
286 }
287
288 public void setAutoIncrement(boolean autoIncrement)
289 {
290 m_autoIncrement = autoIncrement;
291 }
292
293 public String getSequenceName()
294 {
295 return m_sequenceName;
296 }
297
298 public void setSequenceName(String sequenceName)
299 {
300 m_sequenceName = sequenceName;
301 }
302
303 public boolean isPrimaryKey()
304 {
305 return m_IsKeyField;
306 }
307
308 public void setPrimaryKey(boolean b)
309 {
310 m_IsKeyField = b;
311 }
312
313 public int getColNo()
314 {
315 return m_ColNo;
316 }
317
318 /**
319 * Gets the locking.
320 * @return Returns a boolean
321 */
322 public boolean isLocking()
323 {
324 return m_locking;
325 }
326
327 /**
328 * Sets the locking.
329 * @param locking The locking to set
330 */
331 public void setLocking(boolean locking)
332 {
333 m_locking = locking;
334 }
335
336 /**
337 * Gets the updateLock
338 * updateLock controls whether the lock fields should be
339 * updated by OJB when a row is saved
340 * If false then the dbms needs to update the lock fields.
341 * The default is true
342 * @return Returns a boolean
343 */
344 public boolean isUpdateLock()
345 {
346 return updateLock;
347 }
348
349 /**
350 * Sets the updateLock
351 * updateLock controls whether the lock fields should be
352 * updated by OJB when a row is saved.
353 * If false then the dbms needs to update the lock fields.
354 * The default is true
355 * @param updateLock The updateLock to set
356 */
357 public void setUpdateLock(boolean updateLock)
358 {
359 this.updateLock = updateLock;
360 }
361
362 public void setLength(int length)
363 {
364 this.length = length;
365 }
366
367 public int getLength()
368 {
369 return this.length;
370 }
371
372 public void setPrecision(int precision)
373 {
374 this.precision = precision;
375 }
376
377 public int getPrecision()
378 {
379 return this.precision;
380 }
381
382 public void setScale(int scale)
383 {
384 this.scale = scale;
385 }
386
387 public int getScale()
388 {
389 return this.scale;
390 }
391
392 public boolean isRequired()
393 {
394 return required;
395 }
396
397 public void setRequired(boolean required)
398 {
399 this.required = required;
400 }
401
402 public boolean isScaleSpecified()
403 {
404 return scaleSpecified;
405 }
406
407 public void setScaleSpecified(boolean scaleSpecified)
408 {
409 this.scaleSpecified = scaleSpecified;
410 }
411
412 public boolean isPrecisionSpecified()
413 {
414 return precisionSpecified;
415 }
416
417 public void setPrecisionSpecified(boolean precisionSpecified)
418 {
419 this.precisionSpecified = precisionSpecified;
420 }
421
422 public boolean isLengthSpecified()
423 {
424 return lengthSpecified;
425 }
426
427 public void setLengthSpecified(boolean lengthSpecified)
428 {
429 this.lengthSpecified = lengthSpecified;
430 }
431
432 public String getAccess()
433 {
434 return m_access;
435 }
436
437 public void setAccess(String access)
438 {
439 if (access == null)
440 {
441 access = ACCESS_READWRITE;
442 }
443
444 if (ACCESS_ANONYMOUS.equals(access) ||
445 ACCESS_READONLY.equals(access) ||
446 ACCESS_READWRITE.equals(access))
447 {
448 m_access = access;
449 }
450 else
451 {
452 throw new OJBRuntimeException("Try to set unkown field 'access' value: " + access);
453 }
454 }
455
456 public boolean isAccessReadOnly()
457 {
458 return ACCESS_READONLY.equals(getAccess());
459 }
460
461 /**
462 * Returns <em>true</em> if this field is declared as anonymous field.
463 */
464 public boolean isAnonymous()
465 {
466 return AnonymousPersistentField.class.isAssignableFrom(getPersistentField().getClass()) ? true : false;
467 }
468
469 /*
470 * @see XmlCapable#toXML()
471 */
472 public String toXML()
473 {
474 RepositoryTags tags = RepositoryTags.getInstance();
475 String eol = SystemUtils.LINE_SEPARATOR;
476
477 //opening tag + attributes
478 StringBuffer result = new StringBuffer( 1024 );
479 result.append( " " );
480 result.append( tags.getOpeningTagNonClosingById( FIELD_DESCRIPTOR ) );
481 result.append( " " );
482 result.append( eol );
483
484 // // id
485 // String id = new Integer(getColNo()).toString();
486 // result += /*" " +*/ tags.getAttribute(ID, id) + eol;
487
488 // name
489 result.append( " " );
490 result.append( tags.getAttribute( FIELD_NAME, this.getAttributeName() ) );
491 result.append( eol );
492
493 // table not yet implemented
494
495 // column
496 result.append( " " );
497 result.append( tags.getAttribute( COLUMN_NAME, this.getColumnName() ) );
498 result.append( eol );
499
500 // jdbc-type
501 result.append( " " );
502 result.append( tags.getAttribute( JDBC_TYPE, this.getColumnType() ) );
503 result.append( eol );
504
505 // primarykey
506 if( this.isPrimaryKey() )
507 {
508 result.append( " " );
509 result.append( tags.getAttribute( PRIMARY_KEY, "true" ) );
510 result.append( eol );
511 }
512
513 // nullable
514 if( this.isRequired() )
515 {
516 result.append( " " );
517 result.append( tags.getAttribute( NULLABLE, "false" ) );
518 result.append( eol );
519 }
520
521 // indexed not yet implemented
522
523 // autoincrement
524 if( this.isAutoIncrement() )
525 {
526 result.append( " " );
527 result.append( tags.getAttribute( AUTO_INCREMENT, "true" ) );
528 result.append( eol );
529 }
530
531 // locking
532 if( this.isLocking() )
533 {
534 result.append( " " );
535 result.append( tags.getAttribute( LOCKING, "true" ) );
536 result.append( eol );
537 }
538
539 // updateLock
540 // default is true so only write if false
541 if( !this.isUpdateLock() )
542 {
543 result.append( " " );
544 result.append( tags.getAttribute( UPDATE_LOCK, "false" ) );
545 result.append( eol );
546 }
547
548 // default-fetch not yet implemented
549
550 // conversion
551 if( this.getFieldConversion().getClass() != FieldConversionDefaultImpl.class )
552 {
553 result.append( " " );
554 result.append( tags.getAttribute( FIELD_CONVERSION, getFieldConversion().getClass().getName() ) );
555 result.append( eol );
556 }
557
558 // length
559 if( this.isLengthSpecified() )
560 {
561 result.append( " " );
562 result.append( tags.getAttribute( LENGTH, "" + getLength() ) );
563 result.append( eol );
564 }
565
566 // precision
567 if( this.isPrecisionSpecified() )
568 {
569 result.append( " " );
570 result.append( tags.getAttribute( PRECISION, "" + getPrecision() ) );
571 result.append( eol );
572 }
573
574 // scale
575 if( this.isScaleSpecified() )
576 {
577 result.append( " " );
578 result.append( tags.getAttribute( SCALE, "" + getScale() ) );
579 result.append( eol );
580 }
581
582 // access
583 result.append( " " );
584 result.append( tags.getAttribute( ACCESS, this.getAccess() ) );
585 result.append( eol );
586
587 result.append( " />" );
588 result.append( eol );
589 return result.toString();
590 }
591
592 public Object clone()
593 {
594 return SerializationUtils.deserialize(SerializationUtils.serialize(this));
595 }
596 }