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    }