View Javadoc

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 }