View Javadoc

1   package org.apache.torque.templates.transformer.om;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  import org.apache.commons.lang.StringUtils;
26  import org.apache.torque.generator.control.ControllerState;
27  import org.apache.torque.generator.source.SourceElement;
28  import org.apache.torque.generator.source.transform.SourceTransformerException;
29  import org.apache.torque.templates.TemplateOptionName;
30  import org.apache.torque.templates.TorqueSchemaAttributeName;
31  import org.apache.torque.templates.TorqueSchemaElementName;
32  
33  /**
34   * Sets the foreign tables for each of the foreign keys,
35   * and define variables, getters and setters for the complexObjectModel.
36   *
37   * So the source elements are (attributes not shown)
38   * foreign-key
39   *   reference
40   *   reference
41   *   ...
42   *
43   * and the result is
44   * foreign-key
45   *   reference
46   *     local-column
47   *       column
48   *     foreign-column
49   *       column
50   *   reference
51   *     local-column
52   *       column
53   *     foreign-column
54   *       column
55   *   ...
56   *   local-field (properties for the field on the local table's database object
57   *                referencing the foreign database object)
58   *   foreign-field (properties for the field on the foreign table's
59   *                  database object referencing the local database objects)
60   *   table (the foreign referenced table)
61   *
62   * On running this transformer, the javaName Attribute on the columns
63   * must be set properly.
64   */
65  public class OMForeignKeyTransformer
66  {
67      public void transform(
68              SourceElement foreignKey,
69              ControllerState controllerState)
70          throws SourceTransformerException
71      {
72          if (!TorqueSchemaElementName.FOREIGN_KEY.getName().equals(
73                  foreignKey.getName()))
74          {
75              throw new IllegalArgumentException("Illegal element Name "
76                      + foreignKey.getName());
77          }
78  
79          SourceElement localTable = foreignKey.getParent();
80          SourceElement database = localTable.getParent();
81  
82          String foreignTableName = (String) foreignKey.getAttribute(
83                  TorqueSchemaAttributeName.FOREIGN_TABLE);
84          SourceElement foreignTable
85                  = FindHelper.findTable(database, foreignTableName, true);
86          foreignKey.getChildren().add(foreignTable);
87  
88          for (SourceElement reference : foreignKey.getChildren(
89                  TorqueSchemaElementName.REFERENCE))
90          {
91              createLocalElementForReference(localTable, reference);
92              createForeignElementForReference(foreignTable, reference);
93          }
94  
95          StringBuilder localParentPath = new StringBuilder();
96          getParentPath(localTable, localParentPath);
97  
98          StringBuilder foreignParentPath = new StringBuilder();
99          getParentPath(foreignTable, foreignParentPath);
100         // create reference only if this table is not in a external-schema
101         // element of the foreign table's database
102         if (foreignParentPath.toString().startsWith(localParentPath.toString()))
103         {
104             addLocalField(foreignKey, controllerState);
105             addLocalFieldInBean(foreignKey, controllerState);
106         }
107 
108         // create backreference only if the foreign table is not
109         // in a external-schema element of this table's database
110         if (localParentPath.toString().startsWith(foreignParentPath.toString()))
111         {
112             addForeignField(foreignKey, controllerState);
113             addForeignFieldInBean(foreignKey, controllerState);
114         }
115 
116         setForeignKeyAttributes(foreignKey, controllerState);
117     }
118 
119     /**
120      * Second pass of the transformation. Performs all steps which require that
121      * the first pass is complete.
122      *
123      * @param foreignKey the element to transform, not null.
124      * @param controllerState the controller state, not null.
125      *
126      * @throws SourceTransformerException if the transformation fails
127      */
128     public void transformSecondPass(
129             SourceElement foreignKey,
130             ControllerState controllerState)
131         throws SourceTransformerException
132     {
133         modifyForeignFieldSecondPass(foreignKey, controllerState);
134     }
135 
136    /**
137     * Create a foreign-field element which describes a field
138     * in the foreign table referencing the local table.
139     * As more than one foreign object can point
140     * to this object, the filed needs to be a collection.
141     *
142      * @param foreignKey the foreign-key element to which the foreign-field
143      *        element should be added.
144      * @param controllerState the current controller state.
145     */
146     private void addForeignField(
147             SourceElement foreignKey,
148             ControllerState controllerState)
149     {
150         SourceElement localTable = foreignKey.getParent();
151         SourceElement foreignTable = foreignKey.getChild(
152                 TorqueSchemaElementName.TABLE);
153         String referencedBySuffix = getForeignReferencedBySuffix(
154                 foreignKey, controllerState);
155         {
156             // the field name for the variable used
157             String foreignFieldName = (String) controllerState.getOption(
158                         TemplateOptionName.OM_FOREIGN_FIELD_NAME_PREFIX)
159                     + localTable.getAttribute(
160                             TableAttributeName.DB_OBJECT_CLASS_NAME)
161                     + controllerState.getOption(
162                             TemplateOptionName.OM_FOREIGN_FIELD_NAME_SUFFIX)
163                     + referencedBySuffix;
164             // the field name to create the name of the getter and setter
165             String getterSetterFieldName
166                     = (String) localTable.getAttribute(
167                             TableAttributeName.DB_OBJECT_CLASS_NAME)
168                         + referencedBySuffix;
169             SourceElement foreignFieldElement
170                     = new SourceElement(
171                             ForeignKeyChildElementName.FOREIGN_FIELD);
172 
173             foreignFieldElement.setAttribute(
174                     JavaFieldAttributeName.FIELD_NAME,
175                     foreignFieldName);
176             foreignFieldElement.setAttribute(
177                     JavaFieldAttributeName.FIELD_ACCESS_MODIFIER,
178                     "protected");
179             {
180                 // the field name to cache the used Criteria
181                 String criteriaFieldName = "last"
182                         + StringUtils.capitalize(getterSetterFieldName)
183                         + "Criteria";
184                 foreignFieldElement.setAttribute(
185                         ForeignKeyChildAttributeName
186                                 .FOREIGN_COLUMN_CRITERIA_CACHE_FIELD,
187                         criteriaFieldName);
188             }
189 
190             String fieldContainedType = (String) localTable.getAttribute(
191                     TorqueSchemaAttributeName.JAVA_NAME);
192             foreignFieldElement.setAttribute(
193                     JavaFieldAttributeName.FIELD_CONTAINED_TYPE,
194                     fieldContainedType);
195 
196             String fieldType = (String) controllerState.getOption(
197                     TemplateOptionName.OM_FOREIGN_FIELD_TYPE)
198                     + "<" + fieldContainedType + ">";
199             foreignFieldElement.setAttribute(
200                     JavaFieldAttributeName.FIELD_TYPE,
201                     fieldType);
202 
203             foreignFieldElement.setAttribute(
204                     JavaFieldAttributeName.DEFAULT_VALUE,
205                     "null");
206             {
207                 String getterName = FieldHelper.getGetterName(
208                             getterSetterFieldName,
209                             fieldType,
210                             controllerState)
211                         + "s";
212                 foreignFieldElement.setAttribute(
213                         JavaFieldAttributeName.GETTER_NAME,
214                         getterName);
215             }
216             {
217                 String setterName = FieldHelper.getSetterName(
218                             getterSetterFieldName)
219                         + "s";
220                 foreignFieldElement.setAttribute(
221                         JavaFieldAttributeName.SETTER_NAME,
222                             setterName);
223             }
224             {
225                 String adderName = FieldHelper.getAdderName(
226                         getterSetterFieldName,
227                         controllerState);
228                 foreignFieldElement.setAttribute(
229                         JavaFieldAttributeName.ADDER_NAME,
230                         adderName);
231             }
232             {
233                 String initializerName = FieldHelper.getInitializerName(
234                         getterSetterFieldName,
235                         controllerState);
236                 foreignFieldElement.setAttribute(
237                         JavaFieldAttributeName.INITIALIZER_NAME,
238                         initializerName);
239             }
240             {
241                 String isInitializedName = FieldHelper.getIsInitializedName(
242                         getterSetterFieldName,
243                         controllerState);
244                 foreignFieldElement.setAttribute(
245                         JavaFieldAttributeName.IS_INITIALIZED_NAME,
246                         isInitializedName);
247             }
248             {
249                 String initType = (String) controllerState.getOption(
250                         TemplateOptionName.OM_FOREIGN_FIELD_INIT_TYPE)
251                     + "<" + fieldContainedType + ">";
252                 foreignFieldElement.setAttribute(
253                         JavaFieldAttributeName.INITIALIZER_TYPE,
254                         initType);
255 
256             }
257             {
258                 String fillerName = FieldHelper.getFillerName(
259                         getterSetterFieldName,
260                         "",
261                         controllerState);
262                 foreignFieldElement.setAttribute(
263                         JavaFieldAttributeName.FILLER_NAME,
264                         fillerName);
265             }
266             {
267                 // Name for a getter in the foreign table to
268                 // retrieve entries in the foreign table plus the joined
269                 // entries in the local table.
270                 String joinGetterFieldName
271                         = StringUtils.capitalize(getterSetterFieldName)
272                         + "sJoin"
273                         + foreignTable.getAttribute(
274                             TorqueSchemaAttributeName.JAVA_NAME);
275                 String joinGetterName = FieldHelper.getGetterName(
276                         joinGetterFieldName,
277                         fieldType,
278                         controllerState);
279                 foreignFieldElement.setAttribute(
280                         ForeignKeyChildAttributeName
281                             .FOREIGN_FIELD_JOIN_GETTER,
282                         joinGetterName);
283             }
284             {
285                 // Name for the doSelectJoinXXX method in the Peer Class
286                 // of the foreign table.
287                 String peerJoinSelectMethodName
288                         = "doSelectJoin"
289                             + foreignTable.getAttribute(
290                                     TorqueSchemaAttributeName.JAVA_NAME)
291                             + referencedBySuffix;
292                 foreignFieldElement.setAttribute(
293                         ForeignKeyChildAttributeName
294                             .PEER_JOIN_SELECT_METHOD,
295                         peerJoinSelectMethodName);
296             }
297             {
298                 // Name for the doSelectJoinAllExceptXXX method
299                 // in the Peer Class of the foreign table.
300                 String peerJoinAllExceptSelectMethodName
301                         = "doSelectJoinAllExcept"
302                             + foreignTable.getAttribute(
303                                     TorqueSchemaAttributeName.JAVA_NAME)
304                             + referencedBySuffix;
305                 foreignFieldElement.setAttribute(
306                         ForeignKeyChildAttributeName
307                             .PEER_JOIN_ALL_EXCEPT_SELECT_METHOD,
308                         peerJoinAllExceptSelectMethodName);
309             }
310             foreignKey.getChildren().add(foreignFieldElement);
311         }
312     }
313 
314     /**
315      * Adds the the filler attribute to a foreign-field element.
316      * This method checks for naming conflicts with the local fields of the
317      * referenced table and modifies the filler name accordingly if a conflict
318      * is found.
319      * The name of all filler attributes of the local-field elements
320      * must be set when this method is called.
321      *
322      * @param foreignKey the foreign-key element which foreign-field
323      *        element should be modified.
324      * @param controllerState the current controller state.
325      */
326     private void modifyForeignFieldSecondPass(
327             SourceElement foreignKey,
328             ControllerState controllerState)
329     {
330         SourceElement foreignFieldElement
331             = foreignKey.getChild(ForeignKeyChildElementName.FOREIGN_FIELD);
332         if (foreignFieldElement == null)
333         {
334             return;
335         }
336         String setterName = (String) foreignFieldElement.getAttribute(
337                 JavaFieldAttributeName.SETTER_NAME);
338         // setter gets a "s" appended, remove that
339         String regularSetterName
340                 = setterName.substring(0, setterName.length() - 1);
341         String fieldName
342                 = FieldHelper.getFieldNameFromSetterName(regularSetterName);
343         String fillerName = FieldHelper.getFillerName(
344                 fieldName,
345                 "",
346                 controllerState);
347          // check whether there is a local-field in the referenced table
348         // which has the same filler name
349         SourceElement referencedTable = foreignKey.getChild(
350                 TorqueSchemaElementName.TABLE);
351         boolean fillerNamingConflictFound = false;
352         for (SourceElement referencedTableForeignKey
353                 : referencedTable.getChildren(
354                         TorqueSchemaElementName.FOREIGN_KEY))
355         {
356             SourceElement referencedTableLocalField
357                     = referencedTableForeignKey.getChild(
358                             ForeignKeyChildElementName.LOCAL_FIELD);
359             if (referencedTableLocalField == null)
360             {
361                 continue;
362             }
363             String referencedTableFiller
364                     = (String) referencedTableLocalField.getAttribute(
365                             JavaFieldAttributeName.FILLER_NAME);
366             if (fillerName.equals(referencedTableFiller))
367             {
368                 fillerNamingConflictFound = true;
369                 break;
370             }
371         }
372         if (fillerNamingConflictFound)
373         {
374             fillerName = FieldHelper.getFillerName(
375                     fieldName,
376                     (String) controllerState.getOption(
377                         TemplateOptionName.OM_FILLER_REFERENCING_DISTICTION),
378                     controllerState);
379         }
380         foreignFieldElement.setAttribute(
381                 JavaFieldAttributeName.FILLER_NAME,
382                 fillerName);
383     }
384 
385     /**
386      * Create a foreign-field-in-bean element which describes the referenced
387      * instances of the local table bean object in the foreign table bean class.
388      * As more than one foreign bean can point to this bean,
389      * the field needs to be a collection.
390      *
391      * @param foreignKey the foreign-key element
392      *        to which the foreign-field-in-bean element should be added.
393      * @param controllerState the current controller state.
394      */
395     private void addForeignFieldInBean(
396             SourceElement foreignKey,
397             ControllerState controllerState)
398     {
399         SourceElement localTable = foreignKey.getParent();
400         String referencedBySuffix = getForeignReferencedBySuffix(
401                 foreignKey, controllerState);
402         // the field name to create the name of the bean getter and setter
403         String beanGetterSetterFieldName
404                 = (String) localTable.getAttribute(
405                         TableAttributeName.BEAN_CLASS_NAME)
406                     + referencedBySuffix;
407 
408         // the field name for the variable used
409         String foreignFieldInBeanName
410                 = (String) controllerState.getOption(
411                         TemplateOptionName.OM_FOREIGN_FIELD_NAME_PREFIX)
412                     + localTable.getAttribute(
413                             TableAttributeName.BEAN_CLASS_NAME)
414                     + controllerState.getOption(
415                             TemplateOptionName.OM_FOREIGN_FIELD_NAME_SUFFIX)
416                     + referencedBySuffix;
417         SourceElement foreignFieldInBeanElement
418                 = new SourceElement(
419                     ForeignKeyChildElementName.FOREIGN_FIELD_IN_BEAN);
420 
421         foreignFieldInBeanElement.setAttribute(
422                 JavaFieldAttributeName.FIELD_NAME,
423                 foreignFieldInBeanName);
424         foreignFieldInBeanElement.setAttribute(
425                 JavaFieldAttributeName.FIELD_ACCESS_MODIFIER,
426                 "protected");
427 
428         String fieldContainedType = (String) localTable.getAttribute(
429                 TableAttributeName.BEAN_CLASS_NAME);
430         foreignFieldInBeanElement.setAttribute(
431                 JavaFieldAttributeName.FIELD_CONTAINED_TYPE,
432                 fieldContainedType);
433 
434         String fieldType = (String) controllerState.getOption(
435                 TemplateOptionName.OM_FOREIGN_FIELD_TYPE)
436                 + "<" + fieldContainedType + ">";
437         foreignFieldInBeanElement.setAttribute(
438                 JavaFieldAttributeName.FIELD_TYPE,
439                 fieldType);
440 
441         {
442             String initType = (String) controllerState.getOption(
443                     TemplateOptionName.OM_FOREIGN_FIELD_INIT_TYPE)
444                     + "<" + fieldContainedType + ">";
445             foreignFieldInBeanElement.setAttribute(
446                     JavaFieldAttributeName.INITIALIZER_TYPE,
447                     initType);
448         }
449 
450         foreignFieldInBeanElement.setAttribute(
451                 JavaFieldAttributeName.DEFAULT_VALUE,
452                 "null");
453         {
454             String getterName = FieldHelper.getGetterName(
455                         beanGetterSetterFieldName,
456                         fieldType,
457                         controllerState)
458                     + "s";
459             foreignFieldInBeanElement.setAttribute(
460                     JavaFieldAttributeName.GETTER_NAME,
461                     getterName);
462         }
463         {
464             String setterName = FieldHelper.getSetterName(
465                         beanGetterSetterFieldName)
466                     + "s";
467             foreignFieldInBeanElement.setAttribute(
468                     JavaFieldAttributeName.SETTER_NAME,
469                         setterName);
470         }
471         foreignKey.getChildren().add(foreignFieldInBeanElement);
472     }
473 
474     /**
475      * Create a local-field-in-bean element which describes the referenced
476      * instance of the foreign table bean object in the local table bean class.
477      * This field will store one instance of the foreign table's bean object.
478      *
479      * @param foreignKey the foreign-key element
480      *        to which the local-field-in-bean element should be added.
481      * @param controllerState the current controller state.
482      */
483     private void addLocalFieldInBean(
484             SourceElement foreignKey,
485             ControllerState controllerState)
486     {
487         SourceElement foreignTable = foreignKey.getChild(
488                 TorqueSchemaElementName.TABLE);
489         String referencedBySuffix = getLocalReferencedBySuffix(
490                 foreignKey, controllerState);
491         String beanGetterSetterFieldName
492                 = (String) foreignTable.getAttribute(
493                         TableAttributeName.BEAN_CLASS_NAME)
494                     + referencedBySuffix;
495         // the field name for the variable used
496         String localBeanFieldName = (String) controllerState.getOption(
497                 TemplateOptionName.OM_LOCAL_FIELD_NAME_PREFIX)
498                 + foreignTable.getAttribute(
499                         TableAttributeName.BEAN_CLASS_NAME)
500                 + controllerState.getOption(
501                         TemplateOptionName.OM_LOCAL_FIELD_NAME_SUFFIX)
502                 + referencedBySuffix;
503         SourceElement localFieldInBeanElement
504                 = new SourceElement(
505                         ForeignKeyChildElementName.LOCAL_FIELD_IN_BEAN);
506 
507         localFieldInBeanElement.setAttribute(
508                 JavaFieldAttributeName.FIELD_NAME,
509                 localBeanFieldName.toString());
510 
511         String fieldType = (String) foreignTable.getAttribute(
512                 TableAttributeName.BEAN_CLASS_NAME);
513         localFieldInBeanElement.setAttribute(
514                 JavaFieldAttributeName.FIELD_TYPE,
515                 fieldType);
516         localFieldInBeanElement.setAttribute(
517                 JavaFieldAttributeName.DEFAULT_VALUE,
518                 "null");
519         {
520             String getterName = FieldHelper.getGetterName(
521                     beanGetterSetterFieldName,
522                     fieldType,
523                     controllerState);
524             localFieldInBeanElement.setAttribute(
525                     JavaFieldAttributeName.GETTER_NAME,
526                     getterName);
527         }
528         {
529             String setterName = FieldHelper.getSetterName(
530                     beanGetterSetterFieldName);
531             localFieldInBeanElement.setAttribute(
532                     JavaFieldAttributeName.SETTER_NAME,
533                     setterName);
534         }
535         foreignKey.getChildren().add(localFieldInBeanElement);
536     }
537 
538     /**
539      * Create a local-field element which describes a field in the local table
540      * referencing the foreign table.
541      * This field will store one instance of the foreign table's java object.
542      *
543      * @param foreignKey the foreign-key element to which the local-field
544      *        element should be added.
545      * @param controllerState the current controller state.
546      */
547     private void addLocalField(
548             SourceElement foreignKey,
549             ControllerState controllerState)
550     {
551         SourceElement foreignTable = foreignKey.getChild(
552                 TorqueSchemaElementName.TABLE);
553         String referencedBySuffix = getLocalReferencedBySuffix(
554                 foreignKey, controllerState);
555         // the field name for the variable used
556         String localFieldName = (String) controllerState.getOption(
557                 TemplateOptionName.OM_LOCAL_FIELD_NAME_PREFIX)
558                 + foreignTable.getAttribute(
559                         TableAttributeName.DB_OBJECT_CLASS_NAME)
560                 + controllerState.getOption(
561                         TemplateOptionName.OM_LOCAL_FIELD_NAME_SUFFIX)
562                 + referencedBySuffix;
563         // the field name to create the getter and setter names
564         String getterSetterFieldName
565                 = (String) foreignTable.getAttribute(
566                         TableAttributeName.DB_OBJECT_CLASS_NAME)
567                     + referencedBySuffix;
568 
569         SourceElement localFieldElement
570                 = new SourceElement(
571                         ForeignKeyChildElementName.LOCAL_FIELD);
572 
573         localFieldElement.setAttribute(
574                 JavaFieldAttributeName.FIELD_NAME,
575                 localFieldName.toString());
576 
577         String fieldType = (String) foreignTable.getAttribute(
578                 TableAttributeName.DB_OBJECT_CLASS_NAME);
579         localFieldElement.setAttribute(
580                 JavaFieldAttributeName.FIELD_TYPE,
581                 fieldType);
582         localFieldElement.setAttribute(
583                 JavaFieldAttributeName.DEFAULT_VALUE,
584                 "null");
585         {
586             String getterName = FieldHelper.getGetterName(
587                     getterSetterFieldName,
588                     fieldType,
589                     controllerState);
590             localFieldElement.setAttribute(
591                     JavaFieldAttributeName.GETTER_NAME,
592                     getterName);
593         }
594         {
595             String setterName = FieldHelper.getSetterName(
596                     getterSetterFieldName);
597             localFieldElement.setAttribute(
598                     JavaFieldAttributeName.SETTER_NAME,
599                     setterName);
600         }
601         {
602             String fillerName = FieldHelper.getFillerName(
603                     getterSetterFieldName,
604                     "",
605                     controllerState);
606             localFieldElement.setAttribute(
607                     JavaFieldAttributeName.FILLER_NAME,
608                     fillerName);
609         }
610         foreignKey.getChildren().add(localFieldElement);
611     }
612 
613     /**
614      * If a foreign table is referenced more than once from this table,
615      * the local field for the foreign key must be qualified by which
616      * field(s) the foreign table is referenced. This method calculates
617      * this qualifying suffix and returns it.
618      *
619      * @param foreignKey the foreign key element for which the suffix
620      *        is calculated, not null.
621      * @param controllerState the current controller state, not null.
622      *
623      * @return the qualifying suffix, or the empty String if none is needed.
624      *         Not null.
625      */
626     private String getLocalReferencedBySuffix(
627             SourceElement foreignKey,
628             ControllerState controllerState)
629     {
630         SourceElement localTable = foreignKey.getParent();
631         String localTableName = (String) localTable.getAttribute(
632                 TorqueSchemaAttributeName.NAME);
633         String foreignTableName = (String) foreignKey.getAttribute(
634                 TorqueSchemaAttributeName.FOREIGN_TABLE);
635 
636         StringBuilder result = new StringBuilder();
637         List<SourceElement> referencesToSameTable
638                 = FindHelper.findForeignKeyByReferencedTable(
639                         localTable,
640                         foreignTableName);
641         if (referencesToSameTable.size() > 1
642                 || foreignKey.getAttribute(
643                         TorqueSchemaAttributeName.FOREIGN_TABLE)
644                     .equals(localTableName))
645         {
646             result.append((String) controllerState.getOption(
647                     TemplateOptionName.OM_LOCAL_FIELD_NAME_RELATED_BY));
648             for (SourceElement reference : foreignKey.getChildren(
649                     TorqueSchemaElementName.REFERENCE))
650             {
651                 SourceElement localColumnElement
652                         = reference.getChildren(
653                                 ReferenceChildElementName.LOCAL_COLUMN)
654                             .get(0);
655                 SourceElement localColumn
656                         = localColumnElement.getChildren(
657                                 TorqueSchemaElementName.COLUMN)
658                             .get(0);
659                 String fieldName = (String) localColumn.getAttribute(
660                         JavaFieldAttributeName.FIELD_NAME);
661                 result.append(
662                         StringUtils.capitalize(fieldName));
663             }
664         }
665         return result.toString();
666     }
667 
668     /**
669      * If a foreign table is referenced more than once from this table,
670      * the foreign field for the foreign key must be qualified by which
671      * field(s) the foreign table is referenced. This method calculates
672      * this qualifying suffix and returns it.
673      *
674      * @param foreignKey the foreign key element for which the suffix
675      *        is calculated, not null.
676      * @param controllerState the current controller state, not null.
677      *
678      * @return the qualifying suffix, or the empty String if none is needed.
679      *         Not null.
680      */
681     private String getForeignReferencedBySuffix(
682             SourceElement foreignKey,
683             ControllerState controllerState)
684     {
685         SourceElement localTable = foreignKey.getParent();
686         String foreignTableName = (String) foreignKey.getAttribute(
687                 TorqueSchemaAttributeName.FOREIGN_TABLE);
688 
689         StringBuilder result = new StringBuilder();
690         List<SourceElement> referencingSameTable
691                 = FindHelper.findForeignKeyByReferencedTable(
692                         localTable,
693                         foreignTableName);
694         if (referencingSameTable.size() > 1)
695         {
696             result.append((String) controllerState.getOption(
697                     TemplateOptionName.OM_FOREIGN_FIELD_NAME_RELATED_BY));
698             for (SourceElement reference : foreignKey.getChildren(
699                     TorqueSchemaElementName.REFERENCE))
700             {
701                 SourceElement localColumnElement
702                         = reference.getChildren(
703                                 ReferenceChildElementName.LOCAL_COLUMN)
704                             .get(0);
705                 SourceElement localColumn
706                         = localColumnElement.getChildren(
707                                 TorqueSchemaElementName.COLUMN)
708                             .get(0);
709                 String fieldName = (String) localColumn.getAttribute(
710                         JavaFieldAttributeName.FIELD_NAME);
711                 result.append(
712                         StringUtils.capitalize(fieldName));
713             }
714         }
715         return result.toString();
716     }
717 
718     /**
719      * Creates the child element "foreign-colum" for the reference element
720      * and adds the foreign column as a child of it.
721      *
722      * @param foreignTable the foreign table element.
723      * @param reference the refenced element to enrich.
724      */
725     private void createForeignElementForReference(
726             SourceElement foreignTable,
727             SourceElement reference)
728     {
729         {
730             String foreignColumnName = (String)
731                     reference.getAttribute(
732                             TorqueSchemaAttributeName.FOREIGN);
733             SourceElement foreignColumnElement
734                     = new SourceElement(
735                             ReferenceChildElementName.FOREIGN_COLUMN);
736             SourceElement column
737                     = FindHelper.findColumn(foreignTable, foreignColumnName);
738             foreignColumnElement.getChildren().add(column);
739             reference.getChildren().add(foreignColumnElement);
740         }
741     }
742 
743     /**
744      * Creates the child element "local-column" for the reference element
745      * and adds the local column as a child of it.
746      *
747      * @param localTable the local table element.
748      * @param reference the refenced element to enrich.
749      */
750     protected void createLocalElementForReference(
751             SourceElement localTable,
752             SourceElement reference)
753     {
754         {
755             String localColumnName = (String)
756                     reference.getAttribute(
757                             TorqueSchemaAttributeName.LOCAL);
758             SourceElement localColumnElement
759                     = new SourceElement(
760                             ReferenceChildElementName.LOCAL_COLUMN);
761             SourceElement column
762                     = FindHelper.findColumn(localTable, localColumnName);
763             localColumnElement.getChildren().add(column);
764             reference.getChildren().add(localColumnElement);
765         }
766     }
767 
768     private void setForeignKeyAttributes(
769             SourceElement foreignKey,
770             ControllerState controllerState)
771     {
772         SourceElement foreignTable = foreignKey.getChild(
773                 TorqueSchemaElementName.TABLE);
774         String referencedBySuffix = getLocalReferencedBySuffix(
775                 foreignKey, controllerState);
776         String foreignKeyGetterName = (String) controllerState.getOption(
777                 TemplateOptionName.OM_FOREIGN_KEY_GETTER_PREFIX)
778             + foreignTable.getAttribute(
779                     TableAttributeName.DB_OBJECT_CLASS_NAME)
780             + controllerState.getOption(
781                     TemplateOptionName.OM_FOREIGN_KEY_GETTER_SUFFIX)
782             + referencedBySuffix;
783         foreignKey.setAttribute(
784                 ForeignKeyAttributeName.FOREIGN_KEY_GETTER,
785                 foreignKeyGetterName);
786 
787         boolean referencesPrimaryKey = false;
788         List<SourceElement> foreignTablePrimaryKeys
789                 = foreignTable.getChild(TableChildElementName.PRIMARY_KEYS)
790                         .getChildren(TorqueSchemaElementName.COLUMN);
791         List<SourceElement> foreignTableForeignKeyColumns
792             = new ArrayList<SourceElement>();
793         for (SourceElement reference
794                 : foreignKey.getChildren(TorqueSchemaElementName.REFERENCE))
795         {
796             SourceElement column = reference.getChild(
797                     ReferenceChildElementName.FOREIGN_COLUMN)
798                         .getChild(TorqueSchemaElementName.COLUMN);
799             foreignTableForeignKeyColumns.add(column);
800         }
801         if (foreignTablePrimaryKeys.size()
802                 == foreignTableForeignKeyColumns.size())
803         {
804             referencesPrimaryKey = true;
805             for (int i = 0; i < foreignTablePrimaryKeys.size(); ++i)
806             {
807                 if (foreignTablePrimaryKeys.get(i)
808                         != foreignTableForeignKeyColumns.get(i))
809                 {
810                     referencesPrimaryKey = false;
811                     break;
812                 }
813             }
814         }
815         foreignKey.setAttribute(
816                 ForeignKeyAttributeName.REFERENCES_PRIMARY_KEY,
817                 referencesPrimaryKey);
818     }
819 
820     private void getParentPath(
821             SourceElement sourceElement,
822             StringBuilder result)
823     {
824         SourceElement parent = sourceElement.getParent();
825         if (parent == null)
826         {
827             return;
828         }
829         result.append(parent.getName());
830         if (TorqueSchemaElementName.EXTERNAL_SCHEMA.getName().equals(
831                 parent.getName()))
832         {
833             result.append("[")
834                 .append(parent.getAttribute(TorqueSchemaAttributeName.FILENAME))
835                 .append("]");
836         }
837         result.append("/");
838         getParentPath(parent, result);
839     }
840 }