View Javadoc

1   /*
2    * Copyright 2006 Outsource Cafe, Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the 'License')
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *    http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an 'AS IS' BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.javagen.revgen.visitor;
17  
18  import java.util.HashSet;
19  import java.util.Set;
20  
21  import org.javagen.agile.core.model.Model;
22  import org.javagen.agile.core.util.StringUtil;
23  import org.javagen.agile.core.visitor.DefaultVisitor;
24  import org.javagen.agile.db.model.Cardinality;
25  import org.javagen.agile.db.model.FkConstraint;
26  import org.javagen.agile.db.model.FkEnum;
27  import org.javagen.agile.oo.model.Reference;
28  import org.javagen.revgen.context.Keys;
29  
30  /***
31   * Post-process DB-to-Java model before templates are generated, making sure model is consistant. 
32   * Doing this in a seperate pass, allows changes to be made to the model
33   * by intermediate processes (ie customized metadata).
34   *
35   * @author Richard Easterling
36   */
37  public class PostProcessVisitor extends DefaultVisitor {
38          
39      private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(PostProcessVisitor.class);
40      
41      public PostProcessVisitor() {
42          Set<String> itinerary = new HashSet<String>();
43          itinerary.add(Reference.DEFAULT_MODEL_TYPE);
44          this.setItinerary(itinerary);
45      }
46      
47      @Override
48      public void visit(Model model) {
49          if (model instanceof Reference) {
50              Reference reference = (Reference)model;
51              referenceOwningSide(reference);
52          }
53      }
54  
55      /***
56       * Determines the owning side of a relationship as follows:
57       * <ol>
58       * <li>if unidirectional then it IS the owning side</li>
59       * <li>if is the foreign key holding side then it IS the owning side</li>
60       * <li>if it is a ManyToMany and <code>owningSide=true</code> set in context then it IS the owning side</li>
61       * <li>if it is a ManyToMany then sort parent tables by name to determine owning side</li>
62       * </ol>
63       * @param reference
64       */
65      protected void referenceOwningSide(Reference reference) {
66          //needed values:
67          Reference reverseReference = reference.getReverse();
68          FkConstraint fkConstraint = (FkConstraint)reference.get(Keys.FK_CONSTRAINT.toString());
69          FkConstraint reverseFkColumn = reverseReference==null ? null : (FkConstraint)reverseReference.get(Keys.FK_CONSTRAINT.toString());
70          Object val = reference.get(Keys.OWNING_SIDE.toString());
71          Boolean owningSideAlreadySet = val==null ? null : Boolean.parseBoolean(val.toString());//works with strings or booleans
72          //sanity checks:
73          if (reverseReference==null)
74              warn(reference+" has no reverseReference set");
75          if (fkConstraint==null)
76              warn(reference+" has no 'fkColumn' set in context");
77          if (reverseReference!=null && reverseFkColumn==null)
78              warn(reverseReference+" has no 'fkColumn' set in context");
79          boolean bidirectional = 
80              reverseReference!=null 
81              && ! Boolean.FALSE.equals(reverseReference.getNavigable())
82              && ! Boolean.FALSE.equals(reference.getNavigable());
83          if (bidirectional && fkConstraint!=null && reverseFkColumn!=null) {
84              boolean holdsFk = FkEnum.IMPORTED.equals(fkConstraint.getFkType());
85              boolean manyToManyPick = 
86                  Cardinality.MANY_TO_MANY.equals(fkConstraint.getCardinality())
87                  && (
88                          Boolean.TRUE.equals(owningSideAlreadySet)
89                          || fkConstraint.getParentTable().getName().compareTo(reverseFkColumn.getParentTable().getName())>0
90                     );
91              if (holdsFk || manyToManyPick)
92                  reference.put(Keys.OWNING_SIDE.toString(), true);
93          } else { //in unidirectional relationships, navigable side is the owner
94              if (Boolean.TRUE.equals(reference.getNavigable()))
95                  reference.put(Keys.OWNING_SIDE.toString(), true);
96          }
97      }
98  
99      protected void warn(String msg) {
100         log.warn(StringUtil.lastToken(this.getClass().getName(),'.')+" - "+msg+" - can't determine relationship owning side properly.");
101     }
102    
103 }