View Javadoc

1   /* (c) Copyright 2003 Caleigo AB, All rights reserved. 
2    * 
3    * This library is free software; you can redistribute it and/or
4    * modify it under the terms of the GNU Lesser General Public
5    * License as published by the Free Software Foundation; either
6    * version 2.1 of the License, or (at your option) any later version.
7    * 
8    * This library is distributed in the hope that it will be useful,
9    * but WITHOUT ANY WARRANTY; without even the implied warranty of
10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   * Lesser General Public License for more details.
12   * 
13   * You should have received a copy of the GNU Lesser General Public
14   * License along with this library; if not, write to the Free Software
15   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16   *  
17   */
18  
19  package org.caleigo.core.service;
20  
21  
22  import java.sql.*;
23  import java.util.*;
24  
25  import org.caleigo.core.*;
26  import org.caleigo.core.meta.*;
27  import org.caleigo.toolkit.log.*;
28  import org.caleigo.toolkit.util.*;
29  
30  /***
31   *
32   * @author  Mattias Hagstrand
33   * @version $Revision: 1.1 $
34   */
35  /* 
36  *
37  * WHEN        WHO               WHY & WHAT
38  * ------------------------------------------------------------------------------
39  * 2002-03-20  Mattias Hagstrand    Creation
40  */
41  public class JDBCMetaProducer implements IMetaProducer
42  {
43      // Constants ---------------------------------------------------------------
44      public final static int MAX_NBR_OF_OVERVIEW_FIELDS = 3;
45      public final static int MAX_NBR_OF_FIELDS_IN_STATIC = 5;
46      
47      private final static int SELECTABLE_SIZE_LIMIT = 20;
48      private final static int LISTABLE_SIZE_LIMIT = 100;
49      private final static int CACHEABLE_SIZE_LIMIT = 1000;
50      private final static int SCANABLE_SIZE_LIMIT = 100000;
51      
52  	// Data members ------------------------------------------------------------
53  	private IProgressReceiver mProgressReceiver;
54  //	private String mDriverName;
55  //	private String mUrl;
56  //	private String mUser;
57  //	private String mPassword;s
58  	private String mCatalog;
59  	private String mSchema;
60      private String mDataSourceName;
61      private List mEntityRelationCodeNames;
62      private Connection mMetaDataConnection, mDataConnection;
63      private boolean mPerformSizesAnalyzis, mRemoveTablePrefixes, mRemoveColumnPrefixes, mReadDefaultValues, mReadTables, mReadViews;
64  	
65  	// Constructors ------------------------------------------------------------
66      public JDBCMetaProducer(IProgressReceiver progressReceiver, Connection metaDataConn, String catalog, String schema, String dataSourceName)
67      {
68          this(progressReceiver, metaDataConn, catalog, schema, dataSourceName, null, false, false, false, true, true, true);
69      }
70      
71      public JDBCMetaProducer(IProgressReceiver progressReceiver, Connection metaDataConn, String catalog, String schema, String dataSourceName,
72              Connection dataConn, boolean performSizesAnalyzis, boolean removeTablePrefixes, boolean removeColumnPrefixes, boolean readDefaultValues, boolean readTables, boolean readViews)
73  	{
74  		mProgressReceiver = progressReceiver;
75          mMetaDataConnection = metaDataConn;
76          mDataConnection = dataConn;
77  		mCatalog = catalog;
78  		mSchema = schema;
79          mDataSourceName = dataSourceName;
80          mPerformSizesAnalyzis = performSizesAnalyzis;
81          mRemoveTablePrefixes = removeTablePrefixes;
82          mRemoveColumnPrefixes = removeColumnPrefixes;
83          mReadDefaultValues = readDefaultValues;
84          mEntityRelationCodeNames = new ArrayList();
85          mReadTables = readTables;
86          mReadViews = readViews;
87      }
88      
89  //    public JDBCMetaProducer(IProgressReceiver progressReceiver, String driverName, String url, String user, String password, String catalog, String schema, String dataSourceName)
90  //	{
91  //		mProgressReceiver = progressReceiver;
92  //		mCatalog = catalog;
93  //		mSchema = schema;
94  //        mDataSourceName = dataSourceName;
95  //        mEntityRelationCodeNames = new ArrayList();
96  //        
97  //        try
98  //        {
99  //            Class.forName(driverName);
100 //            mConnection = DriverManager.getConnection(url, user, password);
101 //        } catch (Exception e)
102 //        {
103 //            Log.printError(this, "Error while producing meta data", e);
104 //        }
105 //    }
106 	
107 	// IMetaProducer implementation --------------------------------------------
108 	public void produce(IMetaConsumer consumer)
109 	{
110 		mEntityRelationCodeNames.clear();
111         
112         Log.print(this, "Analyzing data source " + mDataSourceName+"...");
113         long startTime = System.currentTimeMillis();
114         
115 		try
116         {
117             // Begin meta data extraction
118             DatabaseMetaData dbMetaData = mMetaDataConnection.getMetaData();
119                         
120 			consumer.beginDataSource(mDataSourceName, mDataSourceName, mDataSourceName, "1.0", false);
121 			
122 			Vector tables = new Vector();
123 			int nbrOfTables = 0;
124             int nbrOfViews = 0;
125             ResultSet tablesRS = null;
126             if (mReadTables) 
127             {
128                 tablesRS = dbMetaData.getTables(mCatalog, mSchema, "%", new String[] {"TABLE"});
129     			while (tablesRS.next())
130     			{
131                     String tableName = tablesRS.getString("TABLE_NAME");
132     				tables.add(tableName);
133     				nbrOfTables++;
134     			}
135                 tablesRS.close();
136             }
137 			
138             // KE : Had to do a special search since there seems to be no way to ask if a given Table
139             //      is a View. So we will know that all tables in the tables Vector with an index of 
140             //      nbrOfTables - nbrOfViews or higher actually is a View. We use this later to 
141             //      set the entitdescriptor as readonly (i.e. not CREATABLE, EDITABLE, DELETABLE) 
142             if (mReadViews) 
143             {
144                 tablesRS = dbMetaData.getTables(mCatalog, mSchema, "%", new String[] {"VIEW"});
145                 while (tablesRS.next())
146                 {
147                     String tableName = tablesRS.getString("TABLE_NAME");
148                     tables.add(tableName);
149                     nbrOfViews++;
150                     nbrOfTables++;
151                 }
152                 tablesRS.close();
153             }
154             Log.print(this, "  Found "+nbrOfTables+" tables in data source.");
155 			
156 			HashMap primaryKeys = new HashMap();
157 			HashMap indexes = new HashMap();
158 //			HashMap versionCols = new HashMap();
159             HashMap referenceFields = new HashMap();
160             ArrayList columns = new ArrayList();
161 			for (int i = 0; i < tables.size(); i++)
162             {				
163 				String tableName = (String) tables.get(i);				
164 				primaryKeys.clear();
165 				indexes.clear();
166 //				versionCols.clear();
167                 referenceFields.clear();
168                 columns.clear();
169 			
170 				mProgressReceiver.setProgressMessage("Analyzing table " + tableName + "...");
171                 Log.print(this, "  Analyzing table " + tableName + "...");
172 				
173                 // Fetch fields/columns.
174                 ResultSet columnsRS = dbMetaData.getColumns(mCatalog, mSchema, tableName, "%");
175                 if (columnsRS != null)
176                 {
177                     while (columnsRS.next())
178                         columns.add(columnsRS.getString("COLUMN_NAME"));
179 
180                     columnsRS.close();
181                 }
182                 
183                 // Fetch foreign key relations.
184                 try
185                 {
186                     ResultSet importedKeysRS = dbMetaData.getImportedKeys(mCatalog, mSchema, tableName);
187                     if (importedKeysRS != null)
188                     {
189                         while (importedKeysRS.next())
190                         {
191                             String pkTableName = importedKeysRS.getString("PKTABLE_NAME");
192                             String fkTableName = importedKeysRS.getString("FKTABLE_NAME");
193                             String fkColumnName = importedKeysRS.getString("FKCOLUMN_NAME");
194                             referenceFields.put(fkColumnName, "");
195                         }
196                         importedKeysRS.close();
197                     }
198                 }
199                 catch (Exception e)
200                 {
201                     Log.printWarning(this, "Couldn't get imported keys for table "+tableName, e);
202                 }
203                          
204                 // Fetch primary key fields.       
205                 try
206                 {
207                     ResultSet primaryKeysRS = dbMetaData.getPrimaryKeys(mCatalog, mSchema, tableName);
208                     if (primaryKeysRS != null)
209                     {
210                         while (primaryKeysRS.next())
211                         {
212                             String columnName = primaryKeysRS.getString("COLUMN_NAME");
213                             Log.print(this, "    Found primary key: " + columnName);
214                             primaryKeys.put(columnName, "");
215                         }
216                         primaryKeysRS.close();
217                     }
218                     else
219                         Log.printWarning(this, "getPrimaryKeys returned null");
220                 }
221                 catch (Exception e)
222                 {
223                     Log.printWarning(this, "Couldn't get primary keys for table "+tableName, e);
224                 }
225                 
226                 // Initialize entity data attributes.
227                 int entityType = this.getEntityType(columns, primaryKeys, referenceFields);
228                 int cacheTime = 300;
229                 if(entityType == IEntityDescriptor.STATIC_ENTITY)
230                     cacheTime = 72000;
231                 int entityFlags = this.getTableSizeType(tableName);
232                 // Check if it is a normal Table or a View, If normal table set it to creatable, editable and optionally
233                 // Deletable
234                 if (i < (nbrOfTables - nbrOfViews)) 
235                 {
236                     entityFlags = entityFlags | AbstractEntityDescriptor.CREATABLE | AbstractEntityDescriptor.EDITABLE;
237                     if (entityType == IEntityDescriptor.LINK_ENTITY)
238                         entityFlags |= AbstractEntityDescriptor.DELETABLE;
239                 }
240                 else 
241                 {
242                     ResultSet colRS = dbMetaData.getColumns(mCatalog, mSchema, tableName, "%");
243                     while (colRS.next()) 
244                     {
245                         System.out.println(tableName + ".% -> " + colRS.getString(3) + "." + colRS.getString(4));;
246                     }
247                     colRS.close();
248                 }
249                 
250                     
251                 // Begin entity production.
252                 consumer.beginEntity(this.createCodeName(tableName, false, true, true), tableName, this.createDisplayName(tableName, false, true, true), entityType, entityFlags, cacheTime);
253                 
254                 try
255                 {
256                     ResultSet importedKeysRS = dbMetaData.getImportedKeys(mCatalog, mSchema, tableName);
257                     if (importedKeysRS != null)
258                     {
259                         boolean previousEntityRelationDone = true;
260                         int oldKeySeq = -1;
261                         while (importedKeysRS.next())
262                         {
263                             String pkTableName = importedKeysRS.getString("PKTABLE_NAME");
264                             String pkColumnName = importedKeysRS.getString("PKCOLUMN_NAME");
265                             String fkTableName = importedKeysRS.getString("FKTABLE_NAME");
266                             String fkColumnName = importedKeysRS.getString("FKCOLUMN_NAME");
267                             short keySeq = importedKeysRS.getShort("KEY_SEQ");
268                             
269                             if ((keySeq == 0 || (keySeq == 1 && oldKeySeq != 0)) && !previousEntityRelationDone)
270                             {
271                                 consumer.endEntityRelation();
272                                 previousEntityRelationDone = true;
273                                 oldKeySeq = -1;
274                             }
275                             
276                             oldKeySeq = keySeq;
277                             
278                             Log.print(this, "    Found imported key(" + keySeq + "): " + fkTableName + "." + fkColumnName + "->" + pkTableName + "." + pkColumnName);
279                             if (previousEntityRelationDone)
280                             {
281                                 consumer.beginEntityRelation(fkTableName,
282                                                              pkTableName,
283                                                              this.createEntityRelationSourceName(this.createCodeName(fkTableName, false, true, true), this.createCodeName(pkTableName, false, true, true)),
284                                                              this.createEntityRelationCodeName(this.createCodeName(fkTableName, false, true, true), this.createCodeName(pkTableName, false, true, true)),
285                                                              this.createDisplayName(fkColumnName, false, true, false),
286                                                              this.createDisplayName(fkTableName, false, false, true));
287                                 previousEntityRelationDone = false;
288                             }
289                                 
290                             consumer.addFieldRelation(fkColumnName, pkColumnName);
291 //                            consumer.endEntityRelation();
292                         }
293                         importedKeysRS.close();
294                         
295                         if (!previousEntityRelationDone)
296                             consumer.endEntityRelation();
297                     }
298                     else
299                         Log.printWarning(this, "getImportedKeys returned null");
300                 }
301                 catch (Exception e)
302                 {
303                     Log.printWarning(this, "Couldn't get imported keys for table "+tableName, e);
304                 }
305                 
306                 try
307                 {
308                     ResultSet indexesRS = dbMetaData.getIndexInfo(mCatalog, mSchema, tableName, false, false);
309                     if (indexesRS != null)
310                     {
311                         while (indexesRS.next())
312                         {
313                             String columnName = indexesRS.getString("COLUMN_NAME");
314                             if (columnName != null)
315                             {
316                                 Log.print(this, "    Found index column " + columnName);
317                                 indexes.put(columnName, "");
318                             }
319                         }
320                         indexesRS.close();
321                     }
322                     else
323                         Log.printWarning(this, "getIndexInfo returned null");
324                 }
325                 catch (Exception e)
326                 {
327                     Log.printWarning(this, "Couldn't get index info", e);
328                 }
329 				
330 				columnsRS = dbMetaData.getColumns(mCatalog, mSchema, tableName, "%");
331                 if (columnsRS != null)
332                 {
333                     int index = 0;
334                     int nbrOfOverviewFields = 0;
335                     boolean nameFieldFound = false;
336                     while (columnsRS != null && columnsRS.next())
337                     {
338                         Object columnDef = null;
339                         if (mReadDefaultValues) 
340                             columnDef = columnsRS.getObject("COLUMN_DEF");
341                             
342                         String sourceName = columnsRS.getString("COLUMN_NAME");
343                         DataType dataType = this.translateType(columnsRS.getShort("DATA_TYPE"), columnsRS.getString("TYPE_NAME"));
344 
345                         int flags = 0;
346                         if (columnsRS.getString("IS_NULLABLE").trim().compareTo("NO") == 0)
347                             flags |= IFieldDescriptor.REQUIRED;				
348                         if (primaryKeys.containsKey(sourceName))					
349                             flags |= IFieldDescriptor.IDENTITY_FIELD | IFieldDescriptor.REQUIRED;					
350                         if (indexes.containsKey(sourceName))					
351                             flags |= IFieldDescriptor.INDEXED;					
352 //                        if (versionCols.containsKey(sourceName))
353 //                            flags |= IFieldDescriptor.AUTOGEN;
354 //                        if (referenceFields.containsKey(sourceName))
355 //                            flags |= IFieldDescriptor.REFERENCE_FIELD;
356                         if (sourceName.toUpperCase().indexOf("NAME") != -1 && !nameFieldFound)
357                         {
358                             flags |= IFieldDescriptor.NAME_FIELD | IFieldDescriptor.OVERVIEW_FIELD;
359                             nameFieldFound = true;
360                         }
361                         if (nbrOfOverviewFields < MAX_NBR_OF_OVERVIEW_FIELDS && this.isOverviewFieldType(dataType))
362                         {
363                             flags |= IFieldDescriptor.OVERVIEW_FIELD;
364                             nbrOfOverviewFields++;
365                         }
366 
367                         Log.print(this, "    Found column("+index+") "+sourceName+ " with type "+dataType.getTypeName()+"("+columnsRS.getInt("COLUMN_SIZE")+")");
368                         consumer.addField(this.createCodeName(sourceName, primaryKeys.containsKey(sourceName), false, false), sourceName,
369                                     this.createDisplayName(sourceName, primaryKeys.containsKey(sourceName), false, false), dataType,
370                                     columnsRS.getInt("COLUMN_SIZE"), flags, this.convertDefaultValue(dataType, columnDef));
371                         index++;
372                     }
373 
374                     columnsRS.close();
375                 }
376                 else
377                     Log.printWarning(this, "getColumns returned null");
378                 
379 				//entityDescriptors.add(new CompositeEntityDescriptor(tableName, createDisplayName(tableName), (IFieldDescriptor[]) fieldDescriptors.toArray(new IFieldDescriptor[0])));
380 				consumer.endEntity();
381 				mProgressReceiver.setProgress((int) (((i + 1) / (nbrOfTables * 1.0)) * 100));
382             }
383 			
384 			consumer.endDataSource();
385             Log.print(this, "Data source analysis completed in " + (System.currentTimeMillis()-startTime) + " ms.");
386         }
387         catch (Exception e)
388         {
389             Log.printError(this, "Error while producing meta data", e);
390         }
391 		finally
392 		{
393 			try
394             {
395                 mMetaDataConnection.close();
396             }
397             catch(SQLException sqle)
398             {
399                 sqle.printStackTrace();
400             }
401             
402             try
403             {
404                 if (mDataConnection != null && mDataConnection != mMetaDataConnection)
405                     mDataConnection.close();
406             }
407             catch(SQLException sqle)
408             {
409                 sqle.printStackTrace();
410             }
411 		}
412 	}
413 	
414 	// Help methods ------------------------------------------------------------	
415     protected int getEntityType(ArrayList columns, HashMap primaryKeys, HashMap referenceFields)
416     {
417         boolean allColumnsArePrimaryKeys = true;
418         boolean allPrimaryKeysAreForeignKeys = true;
419         
420         for (int i = 0; i < columns.size(); i++)
421         {
422             if (!primaryKeys.containsKey(columns.get(i)))
423                 allColumnsArePrimaryKeys = false;
424             if (primaryKeys.containsKey(columns.get(i)) && !referenceFields.containsKey(columns.get(i)))
425                 allPrimaryKeysAreForeignKeys = false;
426         }
427         
428         if (allColumnsArePrimaryKeys && allPrimaryKeysAreForeignKeys)
429             return IEntityDescriptor.LINK_ENTITY;
430         if (allPrimaryKeysAreForeignKeys)
431             return IEntityDescriptor.SLAVE_ENTITY;
432         
433         if (columns.size() <= MAX_NBR_OF_FIELDS_IN_STATIC && referenceFields.keySet().size() == 0 &&
434             primaryKeys.keySet().size() == 1)
435         {
436             return IEntityDescriptor.STATIC_ENTITY;
437         }
438         
439         return IEntityDescriptor.MASTER_ENTITY;
440     }
441     
442     /***
443      * Creates a Correct Code name given the source name from the parsed metadata
444      * 
445      * @param sourceName The Source Name for the entity
446      * @param isPrimaryKeyColumn Set to true if the column is part of the Primary Key constraint
447      * @param removePlural Set to true if the method shoul convert pluralis words to singularis
448      * @param isTable true if creating a code name for a table
449      * @return Returns the correct Code Name
450      */
451     protected String createCodeName(String sourceName, boolean isPrimaryKeyColumn, boolean removePlural, boolean isTable)   
452     {
453         // Check if all letters is upper case letters.
454         boolean upperCaseOnly = true;
455         for (int i = 0; upperCaseOnly && i < sourceName.length(); i++)
456             if(Character.isLetter(sourceName.charAt(i)))
457                 upperCaseOnly = upperCaseOnly && Character.isUpperCase(sourceName.charAt(i));
458         
459         // Remove prefix
460         if (isTable && mRemoveTablePrefixes || !isTable && mRemoveColumnPrefixes)
461         {
462             int underscoreIndex = sourceName.indexOf("_");
463             if (underscoreIndex != -1 && underscoreIndex < sourceName.length() - 2)
464             {
465                 sourceName = sourceName.substring(underscoreIndex + 1);
466             }
467         }
468         
469         // Remove whitespaces and set trailing charecter to uppercase
470         StringBuffer codeName = new StringBuffer();
471         boolean lastWasWhiteSpace = true;
472         for(int i = 0; i < sourceName.length(); i++)
473         {
474             if(Character.isWhitespace(sourceName.charAt(i)) || sourceName.charAt(i)=='_')
475                 lastWasWhiteSpace = true;
476             else if(Character.isJavaLetterOrDigit(sourceName.charAt(i)))
477             {
478                 if(lastWasWhiteSpace)
479                     codeName.append(Character.toUpperCase(sourceName.charAt(i)));
480                 else if(upperCaseOnly)
481                     codeName.append(Character.toLowerCase(sourceName.charAt(i)));
482                 else
483                     codeName.append(sourceName.charAt(i));
484                 lastWasWhiteSpace = false;
485             } 
486         }
487          
488         // Remove plural form.    
489         if(removePlural)
490             this.removePlural(codeName);
491             
492         return codeName.toString();
493 
494 //        // If all letters are upper case convert all letters to lower case to
495 //        // avoid strange behaviour when trying to create the display name
496 //        boolean onlyCapitalLetters = true;
497 //        for (int i = 0; onlyCapitalLetters && i < codeName.length(); i++)
498 //            if (Character.isLowerCase(codeName.charAt(i)))
499 //                onlyCapitalLetters = false;
500 //                
501 //        if (onlyCapitalLetters)
502 //        {
503 //            for (int i = 0; i < codeName.length(); i++) 
504 //            {
505 //                if (codeName.charAt(i)=='_') 
506 //                {
507 //                    codeName.deleteCharAt(i);
508 //                    //i++;
509 //                }
510 //                else 
511 //                {
512 //                    codeName.setCharAt(i, Character.toLowerCase(codeName.charAt(i)));
513 //                }
514 //            }
515 //        }
516 //
517 //        if (removePlural)
518 //            this.removePlural(codeName);
519 //        
520 //        if (codeName.length() > 0 && Character.isLowerCase(codeName.charAt(0)))
521 //            codeName.replace(0, 1, String.valueOf(Character.toUpperCase(codeName.charAt(0))));
522 //        
523 //        // Replace all underscores with spaces and return the result
524 //        return codeName.toString().replace('_', (char) Character.SPACE_SEPARATOR);        
525     }
526     
527     /***
528      * Creates a correct Display Name given the source Name from the parsed metadata
529      * 
530      * @param sourceName The source name for the entity
531      * @param isPrimaryKeyColumn Set to true if the column is part of the Primary Key constraint
532      * @param removePlural Set to true if the method shoul convert pluralis words to singularis
533      * @param isTable true if creating a display name for a table
534      * @return Returns the correct Display Name
535      */
536     protected String createDisplayName(String sourceName, boolean isPrimaryKeyColumn, boolean removePlural, boolean isTable)
537     {
538         StringBuffer tempBuffer = new StringBuffer(sourceName);
539         
540         // If all letters are upper case convert all letters to lower case to
541         // avoid strange behaviour when trying to create the display name
542         boolean onlyUppserCaseLetters = true;
543         for (int i = 0; onlyUppserCaseLetters && i < tempBuffer.length(); i++)
544             if (Character.isLowerCase(tempBuffer.charAt(i)))
545                 onlyUppserCaseLetters = false;
546                 
547         if (onlyUppserCaseLetters)
548             for (int i = 0; i < tempBuffer.length(); i++) 
549             {
550                 if (tempBuffer.charAt(i)=='_') 
551                 {
552                     i++;
553                 }
554                 else 
555                 {
556                     tempBuffer.setCharAt(i, Character.toLowerCase(tempBuffer.charAt(i)));
557                 }
558             }
559         
560         // Remove plural
561         if (removePlural)
562             this.removePlural(tempBuffer);
563             
564         // If sourceName isn't a primary key column remove any trailing ID
565         if (!isPrimaryKeyColumn)
566             this.removeTrailingID(tempBuffer);
567 
568         StringBuffer sourceNameBuffer = new StringBuffer(tempBuffer.toString());
569         
570         // Remove prefix
571         if (isTable && mRemoveTablePrefixes || !isTable && mRemoveColumnPrefixes)
572         {
573             int underscoreIndex = sourceNameBuffer.indexOf("_");
574             if (underscoreIndex != -1 && underscoreIndex < sourceNameBuffer.length() - 2)
575             {
576                 String newDisplayName = sourceNameBuffer.substring(underscoreIndex + 1);
577                 sourceNameBuffer = new StringBuffer(newDisplayName);
578                 tempBuffer = new StringBuffer(newDisplayName);
579             }
580         }
581         
582         // Insert spaces between words. The begning of a new word is defined as
583         // an upper case character followed by a lower case character.
584         int nbrOfSpacesInserted = 0;
585         for (int i = 0; i < tempBuffer.length(); i++)
586             if (Character.isUpperCase(sourceNameBuffer.charAt(i + nbrOfSpacesInserted)) 
587                 && i + nbrOfSpacesInserted + 1 < sourceNameBuffer.length() 
588                 && i > 0 
589                 && Character.isLowerCase(sourceNameBuffer.charAt(i + nbrOfSpacesInserted + 1)) 
590                 && sourceNameBuffer.charAt(i + nbrOfSpacesInserted - 1) != ' ')
591             {
592                 nbrOfSpacesInserted++;
593                 sourceNameBuffer.insert(i + nbrOfSpacesInserted - 1, ' ');
594             }
595         
596         if (sourceNameBuffer.length() > 0 && Character.isLowerCase(sourceNameBuffer.charAt(0)))
597             sourceNameBuffer.replace(0, 1, String.valueOf(Character.toUpperCase(sourceNameBuffer.charAt(0))));
598         
599         // Replace all underscores with spaces and return the result
600         return sourceNameBuffer.toString().replace('_', ' ');
601     }
602 
603     /***
604      * Converts a pluralis word to singularis
605      * 
606      * @param stringBuffer The StringBuffer containing the word to process
607      */
608     protected void removePlural(StringBuffer stringBuffer)
609     {
610         // Fix to avoid removing ending s on status fields.
611         if(stringBuffer.toString().endsWith("Status"))
612             return;
613             
614         if (stringBuffer.length() > 3 
615             && Character.toLowerCase(stringBuffer.charAt(stringBuffer.length() - 1)) == 's' 
616             && Character.toLowerCase(stringBuffer.charAt(stringBuffer.length() - 2)) == 'e' 
617             && Character.toLowerCase(stringBuffer.charAt(stringBuffer.length() - 3)) == 'i')
618         {
619             stringBuffer.replace(stringBuffer.length() - 3, stringBuffer.length(), "y");
620         }
621         
622         if (stringBuffer.length() > 1 
623             && Character.toLowerCase(stringBuffer.charAt(stringBuffer.length() - 1)) == 's' 
624             && Character.toLowerCase(stringBuffer.charAt(stringBuffer.length() - 2)) != 's') 
625         {            
626             stringBuffer.deleteCharAt(stringBuffer.length() - 1);
627         }
628     }
629     
630     protected void removeTrailingID(StringBuffer stringBuffer)
631     {
632         if (stringBuffer.toString().indexOf("ID") != -1)
633             stringBuffer.delete(stringBuffer.toString().indexOf("ID"), stringBuffer.toString().indexOf("ID") + 2);
634     }
635     
636 	protected DataType translateType(short type, String typeName)
637 	{
638 		switch (type)
639         {
640 			case Types.CHAR:
641 			case Types.VARCHAR:
642 			case Types.LONGVARCHAR:
643                 return DataType.STRING;
644 			case Types.TINYINT:
645                 return DataType.BYTE;
646 			case Types.SMALLINT:
647                 return DataType.SHORT;
648 			case Types.INTEGER:
649                 return DataType.INTEGER;
650 			case Types.BIGINT:
651                 return DataType.LONG;
652 			case Types.REAL:
653                 return DataType.FLOAT;
654 			case Types.FLOAT:
655 			case Types.DOUBLE:
656                 return DataType.DOUBLE;
657 			case Types.DATE:
658                 return DataType.DATE;
659 			case Types.BOOLEAN:
660 			case Types.BIT:
661                 return DataType.BOOLEAN;
662 			case Types.TIME:                
663             case Types.TIMESTAMP:
664                 return DataType.DATE;
665 			case Types.NUMERIC:
666 			case Types.DECIMAL:
667 				return DataType.BIG_DECIMAL;
668 				
669 				
670             case Types.ARRAY:
671             case Types.BINARY:
672             case Types.BLOB:
673             case Types.CLOB:
674 			case Types.DATALINK:
675             case Types.DISTINCT:
676             case Types.JAVA_OBJECT:
677             case Types.LONGVARBINARY:
678             case Types.NULL:
679             case Types.OTHER:
680             case Types.REF:
681             case Types.STRUCT:
682             case Types.VARBINARY:
683             default:
684 				return this.performTypeRecognition(typeName);
685         }
686 	}
687 	
688 	protected DataType performTypeRecognition(String typeName)
689 	{
690 		String typeNameUpperCase = typeName.toUpperCase();
691 		if (typeNameUpperCase.indexOf("CHAR") != -1 ||
692 			typeNameUpperCase.indexOf("TEXT") != -1)
693 			return DataType.STRING;
694 		
695 		if (typeNameUpperCase.indexOf("DATE") != -1 ||
696 			typeNameUpperCase.indexOf("TIME") != -1)
697 			return DataType.DATE;
698 		
699 		if (typeNameUpperCase.indexOf("IMAGE") != -1)			
700 			return DataType.IMAGE;		
701 		
702 		return DataType.UNKNOWN;
703 	}
704 	
705 	protected Object convertDefaultValue(DataType dataType, Object value)
706 	{
707 		try
708 		{
709             if (value==null)
710             {
711                 return null;
712             }
713             
714             Object result = dataType.convertFrom(value);
715             
716             if (result!=null)
717             {
718                 return result;
719             }
720             
721             // We might have an integer representation in SQLServer that is surrounded
722             // by (). Oracle adds \n after the value. We solve this by removing all non digit characters.  
723             // This should be handled in a more general way. See Unit JDBC issues under CEL in Rats
724             if (Number.class.isAssignableFrom(dataType.getDataClass()))
725             {
726                 String oldValue = value.toString();
727                 StringBuffer newValue = new StringBuffer();
728                 
729                 for (int i=0;i<oldValue.length();i++)
730                 {
731                     if (Character.isDigit(oldValue.charAt(i)) || oldValue.charAt(i) == ',' || oldValue.charAt(i) == '.') 
732                     {
733                         newValue.append(oldValue.charAt(i));
734                     }                    
735                 }
736                  
737                 result = dataType.convertFrom(newValue.toString());
738                 
739                 if (result!=null)
740                 {
741                     return result;
742                 }
743                 
744             }
745             
746             if (java.util.Date.class.isAssignableFrom(dataType.getDataClass()))
747             {
748                 StringBuffer newValue = new StringBuffer();
749                 String oldValue = value.toString();
750     
751                 for (int i=0;i<oldValue.length();i++)
752                 {
753                     if (Character.isDigit(oldValue.charAt(i)) || oldValue.charAt(i) == ' ' || oldValue.charAt(i) == ':' || oldValue.charAt(i) == '-') 
754                     {
755                         newValue.append(oldValue.charAt(i));
756                     }                    
757                 }
758      
759                 result = dataType.convertFrom(newValue.toString());
760     
761                 if (result!=null)
762                 {
763                     return result;
764                 }
765     
766             }
767 
768 			return null; 
769             
770 		}
771 		catch (Throwable t) {}
772 		
773 		// Not good, throw exception instead?!!
774 		return null;
775 	}
776     
777     protected boolean isOverviewFieldType(DataType dataType)
778     {
779         if ((dataType == DataType.IMAGE) ||
780             (dataType == DataType.UNKNOWN))
781             return false;
782         
783         return true;
784     }
785     
786     protected String createEntityRelationSourceName(String referenceCodeName, String targetCodeName)
787     {
788         return this.createEntityRelationCodeName(referenceCodeName, targetCodeName);
789     }
790     
791     protected String createEntityRelationCodeName(String referenceCodeName, String targetCodeName)
792     {
793         String codeName = referenceCodeName + targetCodeName;
794 
795         if (mEntityRelationCodeNames.contains(codeName))
796         {
797             int id = 1;
798             while (mEntityRelationCodeNames.contains(codeName + id))
799                 id++;
800             codeName += id;
801         }
802 
803         mEntityRelationCodeNames.add(codeName);
804         
805         return codeName;
806     }
807     
808     protected int getTableSizeType(String tableName)
809     {
810         if (mPerformSizesAnalyzis)
811         {
812             try
813             {
814                 Statement statement = mDataConnection.createStatement();
815                 if (SQLToolKit.isQuotingIdentifiersByDefault())
816                 	tableName = SQLToolKit.getDefaultQuotingString() + tableName + SQLToolKit.getDefaultQuotingString();
817                         
818                 ResultSet sizeRS = statement.executeQuery("SELECT count(*) FROM " + tableName);
819                 sizeRS.next();
820                 int tableSize = sizeRS.getInt(1);
821                 sizeRS.close();
822                 
823                 if (tableSize < SELECTABLE_SIZE_LIMIT)
824                     return AbstractEntityDescriptor.SELECTABLE;
825                 if (tableSize < LISTABLE_SIZE_LIMIT)
826                     return AbstractEntityDescriptor.LISTABLE;
827                 if (tableSize < CACHEABLE_SIZE_LIMIT)
828                     return AbstractEntityDescriptor.CACHEABLE;
829                 if (tableSize < SCANABLE_SIZE_LIMIT)
830                     return AbstractEntityDescriptor.SCANABLE;
831                     
832             } catch (SQLException e)
833             {
834                 Log.printError(this.getClass().getName(), "Error reading table size", e);
835             }
836         }
837         
838         return AbstractEntityDescriptor.CACHEABLE;
839     }
840 }