Coverage Report - org.apache.ojb.broker.ant.PreparedModel
 
Classes in this File Line Coverage Branch Coverage Complexity
PreparedModel
N/A
N/A
3.4
 
 1  
 package org.apache.ojb.broker.ant;
 2  
 
 3  
 /* Copyright 2004-2005 The Apache Software Foundation
 4  
 *
 5  
 * Licensed under the Apache License, Version 2.0 (the "License");
 6  
 * you may not use this file except in compliance with the License.
 7  
 * You may obtain a copy of the License at
 8  
 *
 9  
 *     http://www.apache.org/licenses/LICENSE-2.0
 10  
 *
 11  
 * Unless required by applicable law or agreed to in writing, software
 12  
 * distributed under the License is distributed on an "AS IS" BASIS,
 13  
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
 * See the License for the specific language governing permissions and
 15  
 * limitations under the License.
 16  
 */
 17  
 
 18  
 import java.util.ArrayList;
 19  
 import java.util.Arrays;
 20  
 import java.util.HashMap;
 21  
 import java.util.HashSet;
 22  
 import java.util.Iterator;
 23  
 import java.util.List;
 24  
 import java.util.Map;
 25  
 import java.util.Set;
 26  
 import java.util.TreeMap;
 27  
 
 28  
 import org.apache.commons.beanutils.DynaBean;
 29  
 import org.apache.ddlutils.model.Column;
 30  
 import org.apache.ddlutils.model.Database;
 31  
 import org.apache.ddlutils.model.Table;
 32  
 import org.apache.ojb.broker.metadata.ClassDescriptor;
 33  
 import org.apache.ojb.broker.metadata.CollectionDescriptor;
 34  
 import org.apache.ojb.broker.metadata.DescriptorRepository;
 35  
 import org.apache.ojb.broker.metadata.FieldDescriptor;
 36  
 
 37  
 /**
 38  
  * Provides a model derived from {@link org.apache.ojb.broker.metadata.DescriptorRepository} that
 39  
  * is preprocessed for data handling (inserting data, generating data dtd).
 40  
  * 
 41  
  * @author Thomas Dudziak
 42  
  */
 43  
 public class PreparedModel
 44  
 {
 45  
     /** The database model. */
 46  
     private Database _schema;
 47  
     /** Maps dtd elements to tables */
 48  
     private TreeMap _elementToTable                 = new TreeMap();
 49  
     /** Maps dtd elements to lists of class descriptors (which all map to the same table) */
 50  
     private HashMap _elementToClassDescriptors      = new HashMap();
 51  
     /** Maps dtd elements to colum maps which in turn map attribute names to columns */
 52  
     private HashMap _elementToColumnMap             = new HashMap();
 53  
     /** Maps dtd elements to maps that specify which attributes are required */
 54  
     private HashMap _elementToRequiredAttributesMap = new HashMap();
 55  
 
 56  
     public PreparedModel(DescriptorRepository model, Database schema)
 57  
     {
 58  
         _schema = schema;
 59  
         prepareModel(model);
 60  
     }
 61  
 
 62  
     public Iterator getElementNames()
 63  
     {
 64  
         return _elementToTable.keySet().iterator();
 65  
     }
 66  
 
 67  
     public Iterator getAttributeNames(String elementName)
 68  
     {
 69  
         Map columns = getColumnsFor(elementName);
 70  
 
 71  
         return columns == null ? null : columns.keySet().iterator();
 72  
     }
 73  
 
 74  
     public Map getRequiredAttributes(String elementName)
 75  
     {
 76  
         return (Map)_elementToRequiredAttributesMap.get(elementName);
 77  
     }
 78  
 
 79  
     public boolean isRequired(String elementName, String attributeName)
 80  
     {
 81  
         Map requiredAttributes = getRequiredAttributes(elementName);
 82  
 
 83  
         if (requiredAttributes == null)
 84  
         {
 85  
             return false;
 86  
         }
 87  
         else
 88  
         {
 89  
             Boolean status = (Boolean)requiredAttributes.get(attributeName);
 90  
 
 91  
             return status == null ? false : status.booleanValue();
 92  
         }
 93  
     }
 94  
 
 95  
     public Table getTableFor(String elementName)
 96  
     {
 97  
         return (Table)_elementToTable.get(elementName);
 98  
     }
 99  
 
 100  
     /**
 101  
      * Creates a dyna bean for the table associated to the given element.
 102  
      * 
 103  
      * @param elementName The element name
 104  
      * @return The dyna bean
 105  
      */
 106  
     public DynaBean createBeanFor(String elementName)
 107  
     {
 108  
         return _schema.createDynaBeanFor(getTableFor(elementName));
 109  
     }
 110  
     
 111  
     public List getClassDescriptorsMappingTo(String elementName)
 112  
     {
 113  
         return (List)_elementToClassDescriptors.get(elementName);
 114  
     }
 115  
 
 116  
     public Map getColumnsFor(String elementName)
 117  
     {
 118  
         return (Map)_elementToColumnMap.get(elementName);
 119  
     }
 120  
 
 121  
     public Column getColumnFor(String elementName, String attrName)
 122  
     {
 123  
         Map columns = getColumnsFor(elementName);
 124  
 
 125  
         if (columns == null)
 126  
         {
 127  
             return null;
 128  
         }
 129  
         else
 130  
         {
 131  
             return (Column)columns.get(attrName);
 132  
         }
 133  
     }
 134  
 
 135  
     /**
 136  
      * Prepares a representation of the model that is easier accessible for our purposes.
 137  
      * 
 138  
      * @param model  The original model
 139  
      * @return The model representation
 140  
      */
 141  
     private void prepareModel(DescriptorRepository model)
 142  
     {
 143  
         TreeMap result = new TreeMap();
 144  
 
 145  
         for (Iterator it = model.getDescriptorTable().values().iterator(); it.hasNext();)
 146  
         {
 147  
             ClassDescriptor classDesc = (ClassDescriptor)it.next();
 148  
 
 149  
             if (classDesc.getFullTableName() == null)
 150  
             {
 151  
                 // not mapped to a database table
 152  
                 continue;
 153  
             }
 154  
 
 155  
             String elementName        = getElementName(classDesc);
 156  
             Table  mappedTable        = getTableFor(elementName);
 157  
             Map    columnsMap         = getColumnsFor(elementName);
 158  
             Map    requiredAttributes = getRequiredAttributes(elementName);
 159  
             List   classDescs         = getClassDescriptorsMappingTo(elementName);
 160  
 
 161  
             if (mappedTable == null)
 162  
             {
 163  
                 mappedTable = _schema.findTable(classDesc.getFullTableName());
 164  
                 if (mappedTable == null)
 165  
                 {
 166  
                     continue;
 167  
                 }
 168  
                 columnsMap         = new TreeMap();
 169  
                 requiredAttributes = new HashMap();
 170  
                 classDescs         = new ArrayList();
 171  
                 _elementToTable.put(elementName, mappedTable);
 172  
                 _elementToClassDescriptors.put(elementName, classDescs);
 173  
                 _elementToColumnMap.put(elementName, columnsMap);
 174  
                 _elementToRequiredAttributesMap.put(elementName, requiredAttributes);
 175  
             }
 176  
             classDescs.add(classDesc);
 177  
             extractAttributes(classDesc, mappedTable, columnsMap, requiredAttributes);
 178  
         }
 179  
         extractIndirectionTables(model, _schema);
 180  
     }
 181  
 
 182  
     private void extractAttributes(ClassDescriptor classDesc, Table mappedTable, Map columnsMap, Map requiredColumnsMap)
 183  
     {
 184  
         FieldDescriptor[] fieldDescs = classDesc.getFieldDescriptions();
 185  
 
 186  
         if (fieldDescs != null)
 187  
         {
 188  
             for (int idx = 0; idx < fieldDescs.length; idx++)
 189  
             {
 190  
                 Column column = mappedTable.findColumn(fieldDescs[idx].getColumnName());
 191  
 
 192  
                 if (column != null)
 193  
                 {
 194  
                     // we'll check whether another field (of not necessarily the same name)
 195  
                     // already maps to this column; if this is the case, we're ignoring
 196  
                     // this field
 197  
                     boolean alreadyMapped = false;
 198  
 
 199  
                     for (Iterator mappedColumnsIt = columnsMap.values().iterator(); mappedColumnsIt.hasNext();)
 200  
                     {
 201  
                         if (column.equals(mappedColumnsIt.next()))
 202  
                         {
 203  
                             alreadyMapped = true;
 204  
                             break;
 205  
                         }
 206  
                     }
 207  
                     if (!alreadyMapped)
 208  
                     {
 209  
                         String shortAttrName = getShortAttributeName(fieldDescs[idx].getAttributeName());
 210  
         
 211  
                         columnsMap.put(shortAttrName, column);
 212  
                         requiredColumnsMap.put(shortAttrName,
 213  
                                                fieldDescs[idx].isPrimaryKey() ? Boolean.TRUE : Boolean.FALSE);
 214  
                     }
 215  
                 }
 216  
             }
 217  
         }
 218  
     }
 219  
 
 220  
     /**
 221  
      * Extracts indirection tables from the given class descriptor, and adds elements
 222  
      * for them. In contrast to normal elements, for indirection tables the element name
 223  
      * matches the table name, and the attribute names match the column names.
 224  
      * 
 225  
      * @param model    The model
 226  
      * @param elements The elements
 227  
      */
 228  
     private void extractIndirectionTables(DescriptorRepository model, Database schema)
 229  
     {
 230  
         HashMap indirectionTables = new HashMap();
 231  
 
 232  
         // first we gather all participants for each m:n relationship
 233  
         for (Iterator classDescIt = model.getDescriptorTable().values().iterator(); classDescIt.hasNext();)
 234  
         {
 235  
             ClassDescriptor classDesc = (ClassDescriptor)classDescIt.next();
 236  
 
 237  
             for (Iterator collDescIt = classDesc.getCollectionDescriptors().iterator(); collDescIt.hasNext();)
 238  
             {
 239  
                 CollectionDescriptor collDesc   = (CollectionDescriptor)collDescIt.next();
 240  
                 String               indirTable = collDesc.getIndirectionTable();
 241  
 
 242  
                 if ((indirTable != null) && (indirTable.length() > 0))
 243  
                 {
 244  
                     Set columns = (Set)indirectionTables.get(indirTable);
 245  
 
 246  
                     if (columns == null)
 247  
                     {
 248  
                         columns = new HashSet();
 249  
                         indirectionTables.put(indirTable, columns);
 250  
                     }
 251  
                     columns.addAll(Arrays.asList(collDesc.getFksToThisClass()));
 252  
                     columns.addAll(Arrays.asList(collDesc.getFksToItemClass()));
 253  
                 }
 254  
             }
 255  
         }
 256  
         if (indirectionTables.isEmpty())
 257  
         {
 258  
             // nothing to do
 259  
             return;
 260  
         }
 261  
 
 262  
         for (Iterator it = indirectionTables.keySet().iterator(); it.hasNext();)
 263  
         {
 264  
             String tableName   = (String)it.next();
 265  
             Set    columns     = (Set)indirectionTables.get(tableName);
 266  
             String elementName = tableName;
 267  
 
 268  
             for (Iterator classDescIt = model.getDescriptorTable().values().iterator(); classDescIt.hasNext();)
 269  
             {
 270  
                 ClassDescriptor classDesc = (ClassDescriptor)classDescIt.next();
 271  
 
 272  
                 if (tableName.equals(classDesc.getFullTableName()))
 273  
                 {
 274  
                     elementName = getElementName(classDesc);
 275  
 
 276  
                     FieldDescriptor[] fieldDescs = classDesc.getFieldDescriptions();
 277  
 
 278  
                     if (fieldDescs != null)
 279  
                     {
 280  
                         for (int idx = 0; idx < fieldDescs.length; idx++)
 281  
                         {
 282  
                             columns.remove(fieldDescs[idx].getColumnName());
 283  
                         }
 284  
                     }
 285  
                 }
 286  
             }
 287  
 
 288  
             Table mappedTable        = getTableFor(elementName);
 289  
             Map   columnsMap         = getColumnsFor(elementName);
 290  
             Map   requiredAttributes = getRequiredAttributes(elementName);
 291  
     
 292  
             if (mappedTable == null)
 293  
             {
 294  
                 mappedTable = schema.findTable(elementName);
 295  
                 if (mappedTable == null)
 296  
                 {
 297  
                     continue;
 298  
                 }
 299  
                 columnsMap         = new TreeMap();
 300  
                 requiredAttributes = new HashMap();
 301  
                 _elementToTable.put(elementName, mappedTable);
 302  
                 _elementToColumnMap.put(elementName, columnsMap);
 303  
                 _elementToRequiredAttributesMap.put(elementName, requiredAttributes);
 304  
             }
 305  
             for (Iterator columnIt = columns.iterator(); columnIt.hasNext();)
 306  
             {
 307  
                 String columnName = (String)columnIt.next();
 308  
                 Column column     = mappedTable.findColumn(columnName);
 309  
 
 310  
                 if (column != null)
 311  
                 {
 312  
                     columnsMap.put(columnName, column);
 313  
                     requiredAttributes.put(columnName, Boolean.TRUE);
 314  
                 }
 315  
             }
 316  
         }
 317  
     }
 318  
 
 319  
     /**
 320  
      * Returns the element name for the class descriptor which is the adjusted short (unqualified) class
 321  
      * name. Also takes care that the element name does not clash with another class of the same short
 322  
      * name that maps to a different table though.
 323  
      * 
 324  
      * @param classDesc The class descriptor
 325  
      * @return The element name
 326  
      */
 327  
     private String getElementName(ClassDescriptor classDesc)
 328  
     {
 329  
         String elementName = classDesc.getClassNameOfObject().replace('$', '_');
 330  
 
 331  
         elementName = elementName.substring(elementName.lastIndexOf('.') + 1);
 332  
 
 333  
         Table table  = getTableFor(elementName);
 334  
         int   suffix = 0;
 335  
 
 336  
         while ((table != null) && !table.getName().equals(classDesc.getFullTableName()))
 337  
         {
 338  
             ++suffix;
 339  
             table = getTableFor(elementName + "-" + suffix);
 340  
         }
 341  
         if (suffix > 0)
 342  
         {
 343  
             elementName += "-" + suffix;
 344  
         }
 345  
 
 346  
         return elementName;
 347  
     }
 348  
 
 349  
     /**
 350  
      * Adjusts the local attribute name (the part after the last '::' for nested fields).
 351  
      * 
 352  
      * @param attrName The original attribute name
 353  
      * @return The local attribute name
 354  
      */
 355  
     private String getShortAttributeName(String attrName)
 356  
     {
 357  
         return attrName.substring(attrName.lastIndexOf(':') + 1);
 358  
     }
 359  
 }