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.agile.db.model;
17  
18  import java.util.ArrayList;
19  import java.util.List;
20  import javax.xml.bind.annotation.XmlAttribute;
21  import javax.xml.bind.annotation.XmlIDREF;
22  import javax.xml.bind.annotation.XmlRootElement;
23  import javax.xml.bind.annotation.XmlTransient;
24  
25  import org.javagen.agile.core.annotation.DefaultValue;
26  import org.javagen.agile.core.model.AbstractModel;
27  import org.javagen.agile.core.model.Model;
28  
29  /***
30   * This models a foreign key constraint using one or more ColumnReferences to establish the relationship.
31   * @TODO rename this class to FkConstraint!!
32   * 
33   * @author Richard Easterling
34   */
35  @XmlRootElement(name="foreignKeyConstraint")
36  public class FkConstraint extends AbstractModel {
37  	
38      public static final String DEFAULT_MODEL_TYPE = "FKCOLUMN";
39  
40      //	protected Short sequenceNumber;
41  	protected FkEnum fkType;
42  	protected Table targetTable;
43  	protected Cardinality cardinality;
44  	protected FkConstraint reverseFkColumn;
45      protected Boolean cascadeUpdate;
46      protected Boolean cascadeDelete;
47      protected Boolean cascadeInsert;
48  
49  //	<foreign-key foreignTable="VEHICLE_STATUSES" onUpdate="none" onDelete="none">
50  //		<reference foreign="SYSTEM_ID" local="SYSTEM_ID"/>
51  //		<reference foreign="VEHICLE_STATUS_ID" local="VEHICLE_STATUS_ID"/>
52  //	</foreign-key>
53  
54  	public FkConstraint() {
55          super();
56          this.setModelType(DEFAULT_MODEL_TYPE);
57     }
58  	
59  	public FkConstraint(Table parentTable) {
60          this();
61  		this.setParentTable(parentTable);
62  	}
63      
64      /***
65       * Check if foreign key is already managed by a composite primary key, 
66       * if so return <code>false</code>.
67       * 
68       * @see Hibernate In Action section: Foreign keys in composite primary keys
69       * @param fkConstraint
70       * 
71       * TODO verify logic is correct for composite FKs in checkInsertableUpdatable method
72       */
73      public boolean checkInsertableUpdatable() {
74          if (getParentTable().hasCompositeKey())
75              for(ColumnReference ref : this.getColumnReferences()) {
76                  if (ref.getLocalColumn().isKey())
77                      return false;
78              }
79          return true;
80      }
81      
82      /***
83       * Determine if this is a primary key ONE_TO_ONE relationship.
84       */
85      public boolean isPrimaryKeyJoinColumn() {
86          for(ColumnReference ref : this.getColumnReferences()) {
87              if (!(ref.getForeignColumn().isKey() && ref.getLocalColumn().isKey()))
88                  return false;
89          }
90          return true;
91      }
92      
93      /***
94       * Determine if this relationship can be null.
95       */
96      public boolean getNotNull() {
97          for(ColumnReference ref : this.getColumnReferences()) {
98              if (!ref.getLocalColumn().isKey() && ref.getLocalColumn().getNotNull())
99                  return true;
100         }
101         return false;
102     }
103     
104     /***
105      * Unique foreign keys restrict relationships to a ONE_TO_ONE cardinality.
106      * @return true if unique foreign key constraint
107      */
108     public boolean unique() {
109         if (getChildModels()!=null && this.getChildModels().size()==1) {
110             FkConstraint fkHolder = FkEnum.IMPORTED.equals(getFkType()) ? this : this.getReverseFkConstraint();
111             Column localColumn = fkHolder.getColumnReferences().get(0).getLocalColumn();
112             return Boolean.TRUE.equals(localColumn.getUnique());
113         } else {
114             return false;
115         }
116     }
117   
118     @Override
119     public void copyTo(Model targetModel) {
120         FkConstraint target = (FkConstraint)targetModel;
121         super.copyTo(target);
122         if (this.getFkType()!=null)
123             target.setFkType(this.getFkType());
124         if (this.getCardinality()!=null) 
125             target.setCardinality(this.getCardinality());
126         if (this.getCascadeUpdate()!=null)
127             target.setCascadeUpdate(this.getCascadeUpdate());
128         if (this.getCascadeDelete()!=null)
129             target.setCascadeDelete(this.getCascadeDelete());
130     }
131 
132     @XmlTransient
133 	public Table getParentTable() {
134 		return (Table)getParentModel();
135 	}
136 
137     public void setParentTable(Table parentTable) {
138         this.setParentModel(parentTable);
139     }
140 
141     @XmlIDREF
142     public Table getTargetTable() {
143         return targetTable;
144     }
145 
146     public void setTargetTable(Table foreignTable) {
147         this.targetTable = foreignTable;
148     }
149 	public ColumnReference createColumnReference() {
150 		return new ColumnReference(this);
151 	}
152 	
153 	public ColumnReference addColumnReference(Column local, Column foreign) {
154 		ColumnReference ref = createColumnReference();
155 		ref.setForeignColumn(foreign);
156 		ref.setLocalColumn(local);
157 		this.addColumnReference(ref);
158         local.put(ColumnReference.DEFAULT_MODEL_TYPE, ref); //this identifies column as a FK
159 		return ref;
160 	}
161 	
162     public void addColumnReference(ColumnReference reference) {
163         super.addChildModel(reference);
164     }
165     
166     public void setColumnReferences(List<ColumnReference> references) {
167         List<Model> children = new ArrayList<Model>();
168         children.addAll(references);
169         this.setChildModels(children);
170     }
171     
172     @XmlTransient
173     public List<ColumnReference> getColumnReferences() {
174         List<ColumnReference> children = new ArrayList<ColumnReference>();
175         if (getChildModels()!=null) {
176             for(Model child : this.getChildModels()) {
177                 children.add( (ColumnReference)child);
178             }
179         }
180         return children;
181     }
182     
183     public int columnReferencesSize() {
184         return (getChildModels()!=null)  ? getChildModels().size() : 0;
185     }
186     
187     public ColumnReference lookupColumnReference(String pkColName, String fkColName) {
188         if (this.getChildModels()==null)
189             return null;
190         for(Model child : getChildModels()) {
191             ColumnReference reference = (ColumnReference)child;
192             if (reference.getLocalColumn().getName().equalsIgnoreCase(pkColName) &&
193                 reference.getForeignColumn().getName().equalsIgnoreCase(fkColName)) 
194             {
195                 return reference;
196             }
197         }
198         return null;
199     }
200 
201     @XmlAttribute
202 	public FkEnum getFkType() {
203 		return fkType;
204 	}
205 
206 	public void setFkType(FkEnum fkType) {
207 		this.fkType = fkType;
208 	}
209 	
210     /*** Colums that are both PKs and FKs are treated differently (durring updates and inserts for example). */
211     public boolean areLocalColumnsFromPrimaryKey() {
212         boolean result = true;
213         for(Model child : getChildModels()) {
214             ColumnReference ref = (ColumnReference)child;
215             if ( ! ref.getLocalColumn().isKey() )
216                 result = false;
217         }
218         return result;
219     }
220 	
221     @XmlAttribute
222 	public Cardinality getCardinality() {
223 		return cardinality;
224 	}
225 
226 	public void setCardinality(Cardinality cardinality) {
227 		this.cardinality = cardinality;
228         if (reverseFkColumn!=null) { //make sure reverse is consistent
229             Cardinality revCardinality = cardinality.getReverse();
230             if (reverseFkColumn.cardinality != revCardinality)
231                 reverseFkColumn.cardinality = revCardinality;
232         }
233 	}
234 
235     @XmlTransient
236     public String getCardinalityString() {
237         return cardinality==null ? "null" : cardinality.getMapping();
238     }
239 
240     @XmlIDREF
241     public FkConstraint getReverseFkConstraint() {
242 		return reverseFkColumn;
243 	}
244 
245 	public void setReverseFkColumn(FkConstraint reverseFkColumn) {
246 		this.reverseFkColumn = reverseFkColumn;
247 	}
248 
249     @XmlAttribute
250     @DefaultValue("false")
251     public Boolean getCascadeDelete() {
252         return cascadeDelete;
253     }
254 
255     public void setCascadeDelete(Boolean cascadeDelete) {
256         this.cascadeDelete = cascadeDelete;
257     }
258 
259     @XmlAttribute
260     @DefaultValue("false")
261     public Boolean getCascadeUpdate() {
262         return cascadeUpdate;
263     }
264 
265     public void setCascadeUpdate(Boolean cascadeUpdate) {
266         this.cascadeUpdate = cascadeUpdate;
267     }
268 
269     @XmlAttribute
270     @DefaultValue("false")
271     public Boolean getCascadeInsert() {
272         return cascadeInsert;
273     }
274 
275     public void setCascadeInsert(Boolean cascadeInsert) {
276         this.cascadeInsert = cascadeInsert;
277     }
278 	
279 }